mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/DiscussionTools
synced 2024-11-23 16:06:53 +00:00
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:
parent
9f68ee30af
commit
ca5157156a
|
@ -4,6 +4,7 @@
|
||||||
# Build
|
# Build
|
||||||
/vendor/
|
/vendor/
|
||||||
/coverage/
|
/coverage/
|
||||||
|
/docs/
|
||||||
|
|
||||||
# Language files written automatically by TranslateWiki
|
# Language files written automatically by TranslateWiki
|
||||||
/i18n/**/*.json
|
/i18n/**/*.json
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
"rules": {
|
"rules": {
|
||||||
"no-implicit-globals": "off",
|
"no-implicit-globals": "off",
|
||||||
"no-var": "off",
|
"no-var": "off",
|
||||||
|
"prefer-const": "off",
|
||||||
"max-len": "off",
|
"max-len": "off",
|
||||||
"prefer-arrow-callback": "error",
|
"prefer-arrow-callback": "error",
|
||||||
"implicit-arrow-linebreak": "error",
|
"implicit-arrow-linebreak": "error",
|
||||||
|
|
|
@ -64,12 +64,12 @@ function getLatestRevId( pageName ) {
|
||||||
* @return {jQuery.Promise} Promise which resolves with a CommentDetails object, or rejects with an error
|
* @return {jQuery.Promise} Promise which resolves with a CommentDetails object, or rejects with an error
|
||||||
*/
|
*/
|
||||||
CommentController.prototype.getTranscludedFromSource = function () {
|
CommentController.prototype.getTranscludedFromSource = function () {
|
||||||
var pageName = mw.config.get( 'wgRelevantPageName' ),
|
const pageName = mw.config.get( 'wgRelevantPageName' ),
|
||||||
oldId = mw.config.get( 'wgCurRevisionId' ),
|
oldId = mw.config.get( 'wgCurRevisionId' ),
|
||||||
threadItem = this.getThreadItem();
|
threadItem = this.getThreadItem();
|
||||||
|
|
||||||
function followTransclusion( recursionLimit, code, data ) {
|
function followTransclusion( recursionLimit, code, data ) {
|
||||||
var errorData;
|
let errorData;
|
||||||
if ( recursionLimit > 0 && code === 'comment-is-transcluded' ) {
|
if ( recursionLimit > 0 && code === 'comment-is-transcluded' ) {
|
||||||
errorData = data.errors[ 0 ].data;
|
errorData = data.errors[ 0 ].data;
|
||||||
if ( errorData.follow && typeof errorData.transcludedFrom === 'string' ) {
|
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
|
// Arbitrary limit of 10 steps, which should be more than anyone could ever need
|
||||||
// (there are reasonable use cases for at least 2)
|
// (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 ) );
|
.catch( followTransclusion.bind( null, 10 ) );
|
||||||
|
|
||||||
return promise;
|
return promise;
|
||||||
|
@ -114,7 +114,7 @@ CommentController.static.initType = 'page';
|
||||||
* @param {boolean} [suppressNotifications] Don't notify the user if recovering auto-save
|
* @param {boolean} [suppressNotifications] Don't notify the user if recovering auto-save
|
||||||
*/
|
*/
|
||||||
CommentController.prototype.setup = function ( mode, hideErrors, suppressNotifications ) {
|
CommentController.prototype.setup = function ( mode, hideErrors, suppressNotifications ) {
|
||||||
var threadItem = this.getThreadItem();
|
const threadItem = this.getThreadItem();
|
||||||
|
|
||||||
if ( mode === undefined ) {
|
if ( mode === undefined ) {
|
||||||
mode = mw.user.options.get( 'discussiontools-editmode' ) ||
|
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.
|
// we wrap this loading message in another element.
|
||||||
$( '<span>' ).text( mw.msg( 'discussiontools-replywidget-loading' ) )
|
$( '<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
|
// 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.
|
// of preview in source mode (~270px). Visual mode is ~250px.
|
||||||
scrollPaddingCollapsed.bottom += 270;
|
scrollPaddingCollapsed.bottom += 270;
|
||||||
|
@ -230,8 +230,8 @@ CommentController.prototype.onVisibilityChange = function () {
|
||||||
CommentController.prototype.startPoll = function ( nextDelay ) {
|
CommentController.prototype.startPoll = function ( nextDelay ) {
|
||||||
nextDelay = nextDelay || 5000;
|
nextDelay = nextDelay || 5000;
|
||||||
|
|
||||||
var threadItemId = this.threadItem.id;
|
const threadItemId = this.threadItem.id;
|
||||||
var subscribableHeadingId = this.threadItem.getSubscribableHeading().id;
|
const subscribableHeadingId = this.threadItem.getSubscribableHeading().id;
|
||||||
|
|
||||||
this.pollApiRequest = controller.getApi().get( {
|
this.pollApiRequest = controller.getApi().get( {
|
||||||
action: 'discussiontoolscompare',
|
action: 'discussiontoolscompare',
|
||||||
|
@ -245,18 +245,18 @@ CommentController.prototype.startPoll = function ( nextDelay ) {
|
||||||
cmt.author !== mw.user.getName();
|
cmt.author !== mw.user.getName();
|
||||||
}
|
}
|
||||||
|
|
||||||
var result = OO.getProp( response, 'discussiontoolscompare' ) || {};
|
const result = OO.getProp( response, 'discussiontoolscompare' ) || {};
|
||||||
var addedComments = result.addedcomments.filter( relevantCommentFilter );
|
const addedComments = result.addedcomments.filter( relevantCommentFilter );
|
||||||
var removedComments = result.removedcomments.filter( relevantCommentFilter );
|
const removedComments = result.removedcomments.filter( relevantCommentFilter );
|
||||||
|
|
||||||
if ( addedComments.length || removedComments.length ) {
|
if ( addedComments.length || removedComments.length ) {
|
||||||
this.updateNewCommentsWarning( addedComments, removedComments );
|
this.updateNewCommentsWarning( addedComments, removedComments );
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parent comment was deleted
|
// 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)
|
// 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 ) {
|
if ( isParentAdded ) {
|
||||||
this.setParentRemoved( false );
|
this.setParentRemoved( false );
|
||||||
|
@ -410,12 +410,12 @@ CommentController.prototype.onReplyWidgetTeardown = function ( mode ) {
|
||||||
* @return {Object.<string,string>} API query data
|
* @return {Object.<string,string>} API query data
|
||||||
*/
|
*/
|
||||||
CommentController.prototype.getApiQuery = function ( pageName, checkboxes ) {
|
CommentController.prototype.getApiQuery = function ( pageName, checkboxes ) {
|
||||||
var threadItem = this.getThreadItem();
|
const threadItem = this.getThreadItem();
|
||||||
var replyWidget = this.replyWidget;
|
const replyWidget = this.replyWidget;
|
||||||
var sameNameComments = this.threadItemSet.findCommentsByName( threadItem.name );
|
const sameNameComments = this.threadItemSet.findCommentsByName( threadItem.name );
|
||||||
|
|
||||||
var mode = replyWidget.getMode();
|
const mode = replyWidget.getMode();
|
||||||
var tags = [
|
const tags = [
|
||||||
'discussiontools',
|
'discussiontools',
|
||||||
'discussiontools-reply',
|
'discussiontools-reply',
|
||||||
'discussiontools-' + mode
|
'discussiontools-' + mode
|
||||||
|
@ -425,7 +425,7 @@ CommentController.prototype.getApiQuery = function ( pageName, checkboxes ) {
|
||||||
tags.push( 'discussiontools-source-enhanced' );
|
tags.push( 'discussiontools-source-enhanced' );
|
||||||
}
|
}
|
||||||
|
|
||||||
var data = {
|
const data = {
|
||||||
action: 'discussiontoolsedit',
|
action: 'discussiontoolsedit',
|
||||||
paction: 'addcomment',
|
paction: 'addcomment',
|
||||||
page: pageName,
|
page: pageName,
|
||||||
|
@ -448,7 +448,7 @@ CommentController.prototype.getApiQuery = function ( pageName, checkboxes ) {
|
||||||
data.html = replyWidget.getValue();
|
data.html = replyWidget.getValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
var captchaInput = replyWidget.captchaInput;
|
const captchaInput = replyWidget.captchaInput;
|
||||||
if ( captchaInput ) {
|
if ( captchaInput ) {
|
||||||
data.captchaid = captchaInput.getCaptchaId();
|
data.captchaid = captchaInput.getCaptchaId();
|
||||||
data.captchaword = captchaInput.getCaptchaWord();
|
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
|
* @return {jQuery.Promise} Promise which resolves when the save is complete
|
||||||
*/
|
*/
|
||||||
CommentController.prototype.save = function ( pageName ) {
|
CommentController.prototype.save = function ( pageName ) {
|
||||||
var replyWidget = this.replyWidget,
|
const replyWidget = this.replyWidget,
|
||||||
threadItem = this.getThreadItem();
|
threadItem = this.getThreadItem();
|
||||||
|
|
||||||
return this.replyWidget.checkboxesPromise.then( ( checkboxes ) => {
|
return this.replyWidget.checkboxesPromise.then( ( checkboxes ) => {
|
||||||
var data = this.getApiQuery( pageName, checkboxes );
|
const data = this.getApiQuery( pageName, checkboxes );
|
||||||
|
|
||||||
if (
|
if (
|
||||||
// We're saving the first comment on a page that previously didn't exist.
|
// 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,
|
// This means that we might need to redirect to an opaque URL,
|
||||||
// so we must set up query parameters we want ahead of time.
|
// so we must set up query parameters we want ahead of time.
|
||||||
data.returnto = pageName;
|
data.returnto = pageName;
|
||||||
var params = new URLSearchParams();
|
const params = new URLSearchParams();
|
||||||
params.set( 'dtrepliedto', this.getThreadItem().id );
|
params.set( 'dtrepliedto', this.getThreadItem().id );
|
||||||
params.set( 'dttempusercreated', '1' );
|
params.set( 'dttempusercreated', '1' );
|
||||||
data.returntoquery = params.toString();
|
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
|
// 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)
|
// 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;
|
defaults.ajax.timeout = 0;
|
||||||
var noTimeoutApi = new mw.Api( defaults );
|
const noTimeoutApi = new mw.Api( defaults );
|
||||||
|
|
||||||
return mw.libs.ve.targetSaver.postContent(
|
return mw.libs.ve.targetSaver.postContent(
|
||||||
data, { api: noTimeoutApi }
|
data, { api: noTimeoutApi }
|
||||||
|
@ -541,7 +541,7 @@ CommentController.prototype.updateNewCommentsWarning = function ( addedComments,
|
||||||
this.newComments.push.apply( this.newComments, addedComments );
|
this.newComments.push.apply( this.newComments, addedComments );
|
||||||
|
|
||||||
// Delete any comments which have since been deleted (e.g. posted then reverted)
|
// 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(
|
this.newComments = this.newComments.filter(
|
||||||
// If comment ID is not in removedCommentIds, keep it
|
// If comment ID is not in removedCommentIds, keep it
|
||||||
( cmt ) => removedCommentIds.indexOf( cmt.id ) === -1
|
( 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
|
* @return {jQuery.Promise} Promise which resolves when switch is complete
|
||||||
*/
|
*/
|
||||||
CommentController.prototype.switchToWikitext = function () {
|
CommentController.prototype.switchToWikitext = function () {
|
||||||
var oldWidget = this.replyWidget,
|
const oldWidget = this.replyWidget,
|
||||||
target = oldWidget.replyBodyWidget.target,
|
target = oldWidget.replyBodyWidget.target,
|
||||||
oldShowAdvanced = oldWidget.showAdvanced,
|
oldShowAdvanced = oldWidget.showAdvanced,
|
||||||
oldEditSummary = oldWidget.getEditSummary(),
|
oldEditSummary = oldWidget.getEditSummary(),
|
||||||
previewDeferred = $.Deferred();
|
previewDeferred = $.Deferred();
|
||||||
|
|
||||||
// TODO: We may need to pass oldid/etag when editing is supported
|
// 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(
|
this.replyWidgetPromise = this.createReplyWidget(
|
||||||
oldWidget.commentDetails,
|
oldWidget.commentDetails,
|
||||||
{ mode: 'source' }
|
{ mode: 'source' }
|
||||||
|
@ -633,7 +633,7 @@ CommentController.prototype.doIndentReplacements = function ( wikitext, indent )
|
||||||
* @param {Node} rootNode Node potentially containing definition lists (modified in-place)
|
* @param {Node} rootNode Node potentially containing definition lists (modified in-place)
|
||||||
*/
|
*/
|
||||||
CommentController.prototype.undoIndentReplacements = function ( rootNode ) {
|
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
|
// There may be multiple lists when some lines are template generated
|
||||||
children.forEach( ( child ) => {
|
children.forEach( ( child ) => {
|
||||||
if ( child.nodeType === Node.ELEMENT_NODE ) {
|
if ( child.nodeType === Node.ELEMENT_NODE ) {
|
||||||
|
@ -671,7 +671,7 @@ CommentController.prototype.getUnsupportedNodeSelectors = function () {
|
||||||
* @return {jQuery.Promise} Promise which resolves when switch is complete
|
* @return {jQuery.Promise} Promise which resolves when switch is complete
|
||||||
*/
|
*/
|
||||||
CommentController.prototype.switchToVisual = function () {
|
CommentController.prototype.switchToVisual = function () {
|
||||||
var oldWidget = this.replyWidget,
|
let oldWidget = this.replyWidget,
|
||||||
oldShowAdvanced = oldWidget.showAdvanced,
|
oldShowAdvanced = oldWidget.showAdvanced,
|
||||||
oldEditSummary = oldWidget.getEditSummary(),
|
oldEditSummary = oldWidget.getEditSummary(),
|
||||||
wikitext = oldWidget.getValue();
|
wikitext = oldWidget.getValue();
|
||||||
|
@ -684,7 +684,7 @@ CommentController.prototype.switchToVisual = function () {
|
||||||
'$1<span data-dtsignatureforswitching="1"></span>$2'
|
'$1<span data-dtsignatureforswitching="1"></span>$2'
|
||||||
);
|
);
|
||||||
|
|
||||||
var parsePromise;
|
let parsePromise;
|
||||||
if ( wikitext ) {
|
if ( wikitext ) {
|
||||||
wikitext = this.doIndentReplacements( wikitext, dtConf.replyIndentation === 'invisible' ? ':' : '*' );
|
wikitext = this.doIndentReplacements( wikitext, dtConf.replyIndentation === 'invisible' ? ':' : '*' );
|
||||||
|
|
||||||
|
@ -705,17 +705,17 @@ CommentController.prototype.switchToVisual = function () {
|
||||||
);
|
);
|
||||||
|
|
||||||
return $.when( parsePromise, this.replyWidgetPromise ).then( ( html, replyWidget ) => {
|
return $.when( parsePromise, this.replyWidgetPromise ).then( ( html, replyWidget ) => {
|
||||||
var unsupportedSelectors = this.getUnsupportedNodeSelectors();
|
const unsupportedSelectors = this.getUnsupportedNodeSelectors();
|
||||||
|
|
||||||
var doc;
|
let doc;
|
||||||
if ( html ) {
|
if ( html ) {
|
||||||
doc = replyWidget.replyBodyWidget.target.parseDocument( html );
|
doc = replyWidget.replyBodyWidget.target.parseDocument( html );
|
||||||
// Remove RESTBase IDs (T253584)
|
// Remove RESTBase IDs (T253584)
|
||||||
mw.libs.ve.stripRestbaseIds( doc );
|
mw.libs.ve.stripRestbaseIds( doc );
|
||||||
// Check for tables, headings, images, templates
|
// Check for tables, headings, images, templates
|
||||||
for ( var type in unsupportedSelectors ) {
|
for ( const type in unsupportedSelectors ) {
|
||||||
if ( doc.querySelector( unsupportedSelectors[ type ] ) ) {
|
if ( doc.querySelector( unsupportedSelectors[ type ] ) ) {
|
||||||
var $msg = $( '<div>' ).html(
|
const $msg = $( '<div>' ).html(
|
||||||
mw.message(
|
mw.message(
|
||||||
'discussiontools-error-noswitchtove',
|
'discussiontools-error-noswitchtove',
|
||||||
// The following messages are used here:
|
// The following messages are used here:
|
||||||
|
|
|
@ -51,8 +51,8 @@ OO.inheritClass( CommentItem, ThreadItem );
|
||||||
* @return {string} Comment timestamp in standard format
|
* @return {string} Comment timestamp in standard format
|
||||||
*/
|
*/
|
||||||
CommentItem.prototype.getTimestampString = function () {
|
CommentItem.prototype.getTimestampString = function () {
|
||||||
var dtConfig = require( './config.json' );
|
const dtConfig = require( './config.json' );
|
||||||
var switchTime = moment.utc( dtConfig.switchTime );
|
const switchTime = moment.utc( dtConfig.switchTime );
|
||||||
if ( this.timestamp < switchTime ) {
|
if ( this.timestamp < switchTime ) {
|
||||||
return this.timestamp.utc().toISOString();
|
return this.timestamp.utc().toISOString();
|
||||||
} else {
|
} else {
|
||||||
|
@ -65,7 +65,7 @@ CommentItem.prototype.getTimestampString = function () {
|
||||||
* @return {HeadingItem} Closest ancestor which is a HeadingItem
|
* @return {HeadingItem} Closest ancestor which is a HeadingItem
|
||||||
*/
|
*/
|
||||||
CommentItem.prototype.getHeading = function () {
|
CommentItem.prototype.getHeading = function () {
|
||||||
var parent = this;
|
let parent = this;
|
||||||
while ( parent && parent.type !== 'heading' ) {
|
while ( parent && parent.type !== 'heading' ) {
|
||||||
parent = parent.parent;
|
parent = parent.parent;
|
||||||
}
|
}
|
||||||
|
@ -76,7 +76,7 @@ CommentItem.prototype.getHeading = function () {
|
||||||
* @return {HeadingItem|null} Closest heading that can be used for topic subscriptions
|
* @return {HeadingItem|null} Closest heading that can be used for topic subscriptions
|
||||||
*/
|
*/
|
||||||
CommentItem.prototype.getSubscribableHeading = function () {
|
CommentItem.prototype.getSubscribableHeading = function () {
|
||||||
var heading = this.getHeading();
|
let heading = this.getHeading();
|
||||||
while ( heading && heading.type === 'heading' && !heading.isSubscribable() ) {
|
while ( heading && heading.type === 'heading' && !heading.isSubscribable() ) {
|
||||||
heading = heading.parent;
|
heading = heading.parent;
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,11 +22,11 @@ function HeadingItem( range, headingLevel ) {
|
||||||
OO.inheritClass( HeadingItem, ThreadItem );
|
OO.inheritClass( HeadingItem, ThreadItem );
|
||||||
|
|
||||||
HeadingItem.prototype.getLinkableTitle = function () {
|
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 comment is in 0th section, there's no section title for the edit summary
|
||||||
if ( !this.placeholderHeading ) {
|
if ( !this.placeholderHeading ) {
|
||||||
var headline = this.range.startContainer;
|
const headline = this.range.startContainer;
|
||||||
var id = headline.getAttribute( 'id' );
|
const id = headline.getAttribute( 'id' );
|
||||||
if ( id ) {
|
if ( id ) {
|
||||||
// Replace underscores with spaces to undo Sanitizer::escapeIdInternal().
|
// Replace underscores with spaces to undo Sanitizer::escapeIdInternal().
|
||||||
// This assumes that $wgFragmentMode is [ 'html5', 'legacy' ] or [ 'html5' ],
|
// This assumes that $wgFragmentMode is [ 'html5', 'legacy' ] or [ 'html5' ],
|
||||||
|
|
|
@ -40,7 +40,7 @@ LedeSectionDialog.prototype.getSetupProcess = function ( data ) {
|
||||||
// Enable collapsible content (T323639), which is normally not handled on mobile (T111565).
|
// 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
|
// 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.
|
// 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 ) {
|
if ( $collapsible.length ) {
|
||||||
// This module is also preloaded in PageHooks to avoid visual jumps when things collapse.
|
// This module is also preloaded in PageHooks to avoid visual jumps when things collapse.
|
||||||
mw.loader.using( 'jquery.makeCollapsible' ).then( () => {
|
mw.loader.using( 'jquery.makeCollapsible' ).then( () => {
|
||||||
|
|
|
@ -67,12 +67,12 @@ NewTopicController.static.suppressedEditNotices = [
|
||||||
* @inheritdoc
|
* @inheritdoc
|
||||||
*/
|
*/
|
||||||
NewTopicController.prototype.setup = function ( mode ) {
|
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
|
// Insert directly after the page content on already existing pages
|
||||||
// (.mw-parser-output is missing on non-existent pages)
|
// (.mw-parser-output is missing on non-existent pages)
|
||||||
var $parserOutput = this.$pageContainer.find( '.mw-parser-output' ).first();
|
const $parserOutput = this.$pageContainer.find( '.mw-parser-output' ).first();
|
||||||
var $mobileAddTopicWrapper = this.$pageContainer.find( '.ext-discussiontools-init-new-topic' );
|
const $mobileAddTopicWrapper = this.$pageContainer.find( '.ext-discussiontools-init-new-topic' );
|
||||||
if ( $parserOutput.length ) {
|
if ( $parserOutput.length ) {
|
||||||
$parserOutput.after( this.container.$element );
|
$parserOutput.after( this.container.$element );
|
||||||
} else if ( $mobileAddTopicWrapper.length ) {
|
} else if ( $mobileAddTopicWrapper.length ) {
|
||||||
|
@ -109,19 +109,19 @@ NewTopicController.prototype.setupReplyWidget = function ( replyWidget, data ) {
|
||||||
NewTopicController.super.prototype.setupReplyWidget.apply( this, arguments );
|
NewTopicController.super.prototype.setupReplyWidget.apply( this, arguments );
|
||||||
|
|
||||||
this.$notices.empty();
|
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 ) {
|
if ( this.constructor.static.suppressedEditNotices.indexOf( noticeName ) !== -1 ) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
var noticeItem = this.replyWidget.commentDetails.notices[ noticeName ];
|
const noticeItem = this.replyWidget.commentDetails.notices[ noticeName ];
|
||||||
var $noticeElement = $( '<div>' )
|
const $noticeElement = $( '<div>' )
|
||||||
.addClass( 'ext-discussiontools-ui-replyWidget-notice' )
|
.addClass( 'ext-discussiontools-ui-replyWidget-notice' )
|
||||||
.html( typeof noticeItem === 'string' ? noticeItem : noticeItem.message );
|
.html( typeof noticeItem === 'string' ? noticeItem : noticeItem.message );
|
||||||
this.$notices.append( $noticeElement );
|
this.$notices.append( $noticeElement );
|
||||||
}
|
}
|
||||||
mw.hook( 'wikipage.content' ).fire( this.$notices );
|
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() ) {
|
if ( title && !this.sectionTitle.getValue() ) {
|
||||||
// Don't overwrite if the user has already typed something in while the widget was loading.
|
// 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,
|
// 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;
|
this.prevTitleText = title;
|
||||||
|
|
||||||
if ( this.replyWidget.storage.get( 'summary' ) === null ) {
|
if ( this.replyWidget.storage.get( 'summary' ) === null ) {
|
||||||
var generatedSummary = this.generateSummary( title );
|
const generatedSummary = this.generateSummary( title );
|
||||||
this.replyWidget.editSummaryInput.setValue( generatedSummary );
|
this.replyWidget.editSummaryInput.setValue( generatedSummary );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -180,8 +180,8 @@ NewTopicController.prototype.onReplyWidgetClearStorage = function () {
|
||||||
|
|
||||||
NewTopicController.prototype.storeEditSummary = function () {
|
NewTopicController.prototype.storeEditSummary = function () {
|
||||||
if ( this.replyWidget ) {
|
if ( this.replyWidget ) {
|
||||||
var currentSummary = this.replyWidget.editSummaryInput.getValue();
|
const currentSummary = this.replyWidget.editSummaryInput.getValue();
|
||||||
var generatedSummary = this.generateSummary( this.sectionTitle.getValue() );
|
const generatedSummary = this.generateSummary( this.sectionTitle.getValue() );
|
||||||
if ( currentSummary === generatedSummary ) {
|
if ( currentSummary === generatedSummary ) {
|
||||||
// Do not store generated summaries (T315730)
|
// Do not store generated summaries (T315730)
|
||||||
this.replyWidget.storage.remove( 'summary' );
|
this.replyWidget.storage.remove( 'summary' );
|
||||||
|
@ -202,7 +202,7 @@ NewTopicController.prototype.onReplyWidgetTeardown = function ( abandoned ) {
|
||||||
this.container.$element.detach();
|
this.container.$element.detach();
|
||||||
|
|
||||||
if ( mw.config.get( 'wgDiscussionToolsStartNewTopicTool' ) ) {
|
if ( mw.config.get( 'wgDiscussionToolsStartNewTopicTool' ) ) {
|
||||||
var url = new URL( location.href );
|
const url = new URL( location.href );
|
||||||
url.searchParams.delete( 'action' );
|
url.searchParams.delete( 'action' );
|
||||||
url.searchParams.delete( 'veaction' );
|
url.searchParams.delete( 'veaction' );
|
||||||
url.searchParams.delete( 'section' );
|
url.searchParams.delete( 'section' );
|
||||||
|
@ -244,11 +244,11 @@ NewTopicController.prototype.getUnsupportedNodeSelectors = function () {
|
||||||
* @inheritdoc
|
* @inheritdoc
|
||||||
*/
|
*/
|
||||||
NewTopicController.prototype.getApiQuery = function ( pageName, checkboxes ) {
|
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
|
// Rebuild the tags array and remove the reply tag
|
||||||
var tags = ( data.dttags || '' ).split( ',' );
|
const tags = ( data.dttags || '' ).split( ',' );
|
||||||
var replyTag = tags.indexOf( 'discussiontools-reply' );
|
const replyTag = tags.indexOf( 'discussiontools-reply' );
|
||||||
if ( replyTag !== -1 ) {
|
if ( replyTag !== -1 ) {
|
||||||
tags.splice( replyTag, 1 );
|
tags.splice( replyTag, 1 );
|
||||||
}
|
}
|
||||||
|
@ -290,16 +290,16 @@ NewTopicController.prototype.generateSummary = function ( titleText ) {
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
NewTopicController.prototype.onSectionTitleChange = function () {
|
NewTopicController.prototype.onSectionTitleChange = function () {
|
||||||
var titleText = this.sectionTitle.getValue();
|
const titleText = this.sectionTitle.getValue();
|
||||||
var prevTitleText = this.prevTitleText;
|
const prevTitleText = this.prevTitleText;
|
||||||
|
|
||||||
if ( prevTitleText !== titleText ) {
|
if ( prevTitleText !== titleText ) {
|
||||||
this.replyWidget.storage.set( 'title', titleText );
|
this.replyWidget.storage.set( 'title', titleText );
|
||||||
|
|
||||||
var generatedSummary = this.generateSummary( titleText );
|
const generatedSummary = this.generateSummary( titleText );
|
||||||
var generatedPrevSummary = this.generateSummary( prevTitleText );
|
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
|
// Fill in edit summary if it was not modified by the user yet
|
||||||
if ( currentSummary === generatedPrevSummary ) {
|
if ( currentSummary === generatedPrevSummary ) {
|
||||||
|
@ -318,13 +318,13 @@ NewTopicController.prototype.onSectionTitleChange = function () {
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
NewTopicController.prototype.onBodyFocus = function () {
|
NewTopicController.prototype.onBodyFocus = function () {
|
||||||
var offsetBefore = this.replyWidget.$element.offset().top;
|
const offsetBefore = this.replyWidget.$element.offset().top;
|
||||||
var rootScrollable = OO.ui.Element.static.getRootScrollableElement( document.body );
|
const rootScrollable = OO.ui.Element.static.getRootScrollableElement( document.body );
|
||||||
var scrollBefore = rootScrollable.scrollTop;
|
const scrollBefore = rootScrollable.scrollTop;
|
||||||
|
|
||||||
this.checkSectionTitleValidity();
|
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
|
// Ensure the rest of the widget doesn't move when the validation
|
||||||
// message is triggered by a focus. (T275923)
|
// message is triggered by a focus. (T275923)
|
||||||
// Browsers sometimes also scroll in response to focus events,
|
// Browsers sometimes also scroll in response to focus events,
|
||||||
|
|
|
@ -44,7 +44,7 @@ Parser.prototype.parse = function ( rootNode, title ) {
|
||||||
this.rootNode = rootNode;
|
this.rootNode = rootNode;
|
||||||
this.title = title;
|
this.title = title;
|
||||||
|
|
||||||
var result = this.buildThreadItems();
|
const result = this.buildThreadItems();
|
||||||
this.buildThreads( result );
|
this.buildThreads( result );
|
||||||
this.computeIdsAndNames( result );
|
this.computeIdsAndNames( result );
|
||||||
|
|
||||||
|
@ -90,12 +90,12 @@ Parser.prototype.getTimestampRegexp = function ( contLangVariant, format, digits
|
||||||
return '(' + array.map( mw.util.escapeRegExp ).join( '|' ) + ')';
|
return '(' + array.map( mw.util.escapeRegExp ).join( '|' ) + ')';
|
||||||
}
|
}
|
||||||
|
|
||||||
var s = '';
|
let s = '';
|
||||||
var raw = false;
|
let raw = false;
|
||||||
// Adapted from Language::sprintfDate()
|
// Adapted from Language::sprintfDate()
|
||||||
for ( var p = 0; p < format.length; p++ ) {
|
for ( let p = 0; p < format.length; p++ ) {
|
||||||
var num = false;
|
let num = false;
|
||||||
var code = format[ p ];
|
let code = format[ p ];
|
||||||
if ( code === 'x' && p < format.length - 1 ) {
|
if ( code === 'x' && p < format.length - 1 ) {
|
||||||
code += format[ ++p ];
|
code += format[ ++p ];
|
||||||
}
|
}
|
||||||
|
@ -182,7 +182,7 @@ Parser.prototype.getTimestampRegexp = function ( contLangVariant, format, digits
|
||||||
case '"':
|
case '"':
|
||||||
// Quoted literal
|
// Quoted literal
|
||||||
if ( p < format.length - 1 ) {
|
if ( p < format.length - 1 ) {
|
||||||
var endQuote = format.indexOf( '"', p + 1 );
|
const endQuote = format.indexOf( '"', p + 1 );
|
||||||
if ( endQuote === -1 ) {
|
if ( endQuote === -1 ) {
|
||||||
// No terminating quote, assume literal "
|
// No terminating quote, assume literal "
|
||||||
s += '"';
|
s += '"';
|
||||||
|
@ -213,10 +213,10 @@ Parser.prototype.getTimestampRegexp = function ( contLangVariant, format, digits
|
||||||
s += '[\\u200E\\u200F]?';
|
s += '[\\u200E\\u200F]?';
|
||||||
}
|
}
|
||||||
|
|
||||||
var tzRegexp = regexpAlternateGroup( Object.keys( tzAbbrs ) );
|
const tzRegexp = regexpAlternateGroup( Object.keys( tzAbbrs ) );
|
||||||
// Hard-coded parentheses and space like in Parser::pstPass2
|
// Hard-coded parentheses and space like in Parser::pstPass2
|
||||||
// Ignore some invisible Unicode characters that often sneak into copy-pasted timestamps (T245784)
|
// 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;
|
return regexp;
|
||||||
};
|
};
|
||||||
|
@ -235,9 +235,9 @@ Parser.prototype.getTimestampRegexp = function ( contLangVariant, format, digits
|
||||||
* @return {TimestampParser} Timestamp parser function
|
* @return {TimestampParser} Timestamp parser function
|
||||||
*/
|
*/
|
||||||
Parser.prototype.getTimestampParser = function ( contLangVariant, format, digits, localTimezone, tzAbbrs ) {
|
Parser.prototype.getTimestampParser = function ( contLangVariant, format, digits, localTimezone, tzAbbrs ) {
|
||||||
var matchingGroups = [];
|
const matchingGroups = [];
|
||||||
for ( var p = 0; p < format.length; p++ ) {
|
for ( let p = 0; p < format.length; p++ ) {
|
||||||
var code = format[ p ];
|
let code = format[ p ];
|
||||||
if ( code === 'x' && p < format.length - 1 ) {
|
if ( code === 'x' && p < format.length - 1 ) {
|
||||||
code += format[ ++p ];
|
code += format[ ++p ];
|
||||||
}
|
}
|
||||||
|
@ -275,7 +275,7 @@ Parser.prototype.getTimestampParser = function ( contLangVariant, format, digits
|
||||||
case '"':
|
case '"':
|
||||||
// Quoted literal
|
// Quoted literal
|
||||||
if ( p < format.length - 1 ) {
|
if ( p < format.length - 1 ) {
|
||||||
var endQuote = format.indexOf( '"', p + 1 );
|
const endQuote = format.indexOf( '"', p + 1 );
|
||||||
if ( endQuote !== -1 ) {
|
if ( endQuote !== -1 ) {
|
||||||
p = endQuote;
|
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
|
* - {string|null} warning Warning message if the input wasn't correctly formed
|
||||||
*/
|
*/
|
||||||
return ( match ) => {
|
return ( match ) => {
|
||||||
var
|
let
|
||||||
year = 0,
|
year = 0,
|
||||||
monthIdx = 0,
|
monthIdx = 0,
|
||||||
day = 0,
|
day = 0,
|
||||||
hour = 0,
|
hour = 0,
|
||||||
minute = 0;
|
minute = 0;
|
||||||
|
|
||||||
for ( var i = 0; i < matchingGroups.length; i++ ) {
|
for ( let i = 0; i < matchingGroups.length; i++ ) {
|
||||||
var code2 = matchingGroups[ i ];
|
const code2 = matchingGroups[ i ];
|
||||||
var text = match[ i + 1 ];
|
const text = match[ i + 1 ];
|
||||||
|
|
||||||
switch ( code2 ) {
|
switch ( code2 ) {
|
||||||
case 'xg':
|
case 'xg':
|
||||||
|
@ -378,11 +378,11 @@ Parser.prototype.getTimestampParser = function ( contLangVariant, format, digits
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// The last matching group is the timezone abbreviation
|
// 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
|
// 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.
|
// 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,
|
// 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."
|
// "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
|
// Since the timezone abbreviation disambiguates the DST/non-DST times, we can detect when
|
||||||
// that behavior was incorrect...
|
// that behavior was incorrect...
|
||||||
var dateWarning = null;
|
let dateWarning = null;
|
||||||
if ( date.zoneAbbr() !== tzAbbr ) {
|
if ( date.zoneAbbr() !== tzAbbr ) {
|
||||||
// ...and force the correct parsing. I can't find proper documentation for this feature,
|
// ...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
|
// 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 ) {
|
function acceptOnlyNodesAllowingComments( node ) {
|
||||||
if ( node instanceof HTMLElement ) {
|
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
|
// The table of contents has a heading that gets erroneously detected as a section
|
||||||
if ( node.id === 'toc' ) {
|
if ( node.id === 'toc' ) {
|
||||||
return NodeFilter.FILTER_REJECT;
|
return NodeFilter.FILTER_REJECT;
|
||||||
|
@ -493,7 +493,7 @@ function acceptOnlyNodesAllowingComments( node ) {
|
||||||
return NodeFilter.FILTER_REJECT;
|
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)
|
// Don't detect comments within headings (but don't reject the headings themselves)
|
||||||
if ( parentNode instanceof HTMLElement && parentNode.tagName.match( /^h([1-6])$/i ) ) {
|
if ( parentNode instanceof HTMLElement && parentNode.tagName.match( /^h([1-6])$/i ) ) {
|
||||||
return NodeFilter.FILTER_REJECT;
|
return NodeFilter.FILTER_REJECT;
|
||||||
|
@ -515,7 +515,7 @@ function acceptOnlyNodesAllowingComments( node ) {
|
||||||
* - {Object} range Range-like object covering the timestamp
|
* - {Object} range Range-like object covering the timestamp
|
||||||
*/
|
*/
|
||||||
Parser.prototype.findTimestamp = function ( node, timestampRegexps ) {
|
Parser.prototype.findTimestamp = function ( node, timestampRegexps ) {
|
||||||
var matchData, i,
|
let matchData, i,
|
||||||
nodeText = '',
|
nodeText = '',
|
||||||
offset = 0,
|
offset = 0,
|
||||||
// Searched nodes (reverse order)
|
// Searched nodes (reverse order)
|
||||||
|
@ -562,12 +562,12 @@ Parser.prototype.findTimestamp = function ( node, timestampRegexps ) {
|
||||||
if ( matchData ) {
|
if ( matchData ) {
|
||||||
var timestampLength = matchData[ 0 ].length;
|
var timestampLength = matchData[ 0 ].length;
|
||||||
// Bytes at the end of the last node which aren't part of the match
|
// 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
|
// 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.
|
// the timestamp if there is trailing garbage, so that is a negative offset.
|
||||||
var count = -tailLength;
|
var count = -tailLength;
|
||||||
var endContainer = nodes[ 0 ];
|
const endContainer = nodes[ 0 ];
|
||||||
var endOffset = endContainer.nodeValue.length - tailLength;
|
const endOffset = endContainer.nodeValue.length - tailLength;
|
||||||
|
|
||||||
var startContainer, startOffset;
|
var startContainer, startOffset;
|
||||||
// eslint-disable-next-line no-loop-func
|
// eslint-disable-next-line no-loop-func
|
||||||
|
@ -584,7 +584,7 @@ Parser.prototype.findTimestamp = function ( node, timestampRegexps ) {
|
||||||
return false;
|
return false;
|
||||||
} );
|
} );
|
||||||
|
|
||||||
var range = {
|
const range = {
|
||||||
startContainer: startContainer,
|
startContainer: startContainer,
|
||||||
startOffset: startOffset,
|
startOffset: startOffset,
|
||||||
endContainer: endContainer,
|
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)
|
* - {string|null} displayName Display name (link text if link target was in the user namespace)
|
||||||
*/
|
*/
|
||||||
Parser.prototype.getUsernameFromLink = function ( link ) {
|
Parser.prototype.getUsernameFromLink = function ( link ) {
|
||||||
var title;
|
let title;
|
||||||
// Selflink: use title of current page
|
// Selflink: use title of current page
|
||||||
if ( link.classList.contains( 'mw-selflink' ) ) {
|
if ( link.classList.contains( 'mw-selflink' ) ) {
|
||||||
title = this.title;
|
title = this.title;
|
||||||
} else {
|
} else {
|
||||||
var titleString = utils.getTitleFromUrl( link.href ) || '';
|
const titleString = utils.getTitleFromUrl( link.href ) || '';
|
||||||
// Performance optimization, skip strings that obviously don't contain a namespace
|
// Performance optimization, skip strings that obviously don't contain a namespace
|
||||||
if ( !titleString || titleString.indexOf( ':' ) === -1 ) {
|
if ( !titleString || titleString.indexOf( ':' ) === -1 ) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -629,11 +629,11 @@ Parser.prototype.getUsernameFromLink = function ( link ) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var username;
|
let username;
|
||||||
var displayName = null;
|
let displayName = null;
|
||||||
var namespaceId = title.getNamespaceId();
|
const namespaceId = title.getNamespaceId();
|
||||||
var mainText = title.getMainText();
|
const mainText = title.getMainText();
|
||||||
var namespaceIds = mw.config.get( 'wgNamespaceIds' );
|
const namespaceIds = mw.config.get( 'wgNamespaceIds' );
|
||||||
|
|
||||||
if (
|
if (
|
||||||
namespaceId === namespaceIds.user ||
|
namespaceId === namespaceIds.user ||
|
||||||
|
@ -645,17 +645,17 @@ Parser.prototype.getUsernameFromLink = function ( link ) {
|
||||||
}
|
}
|
||||||
if ( namespaceId === namespaceIds.user ) {
|
if ( namespaceId === namespaceIds.user ) {
|
||||||
// Use regex trim for consistency with PHP implementation
|
// 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
|
// Record the display name if it has been customised beyond changing case
|
||||||
if ( text && text.toLowerCase() !== username.toLowerCase() ) {
|
if ( text && text.toLowerCase() !== username.toLowerCase() ) {
|
||||||
displayName = text;
|
displayName = text;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if ( namespaceId === namespaceIds.special ) {
|
} else if ( namespaceId === namespaceIds.special ) {
|
||||||
var parts = mainText.split( '/' );
|
const parts = mainText.split( '/' );
|
||||||
if ( parts.length === 2 && parts[ 0 ] === this.data.specialContributionsName ) {
|
if ( parts.length === 2 && parts[ 0 ] === this.data.specialContributionsName ) {
|
||||||
// Normalize the username: users may link to their contributions with an unnormalized name
|
// 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 ) {
|
if ( !userpage ) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -692,10 +692,10 @@ Parser.prototype.getUsernameFromLink = function ( link ) {
|
||||||
* - {string|null} username Username, null for unsigned comments
|
* - {string|null} username Username, null for unsigned comments
|
||||||
*/
|
*/
|
||||||
Parser.prototype.findSignature = function ( timestampNode, until ) {
|
Parser.prototype.findSignature = function ( timestampNode, until ) {
|
||||||
var sigUsername = null;
|
let sigUsername = null;
|
||||||
var sigDisplayName = null;
|
let sigDisplayName = null;
|
||||||
var length = 0;
|
let length = 0;
|
||||||
var lastLinkNode = timestampNode;
|
let lastLinkNode = timestampNode;
|
||||||
|
|
||||||
utils.linearWalkBackwards(
|
utils.linearWalkBackwards(
|
||||||
timestampNode,
|
timestampNode,
|
||||||
|
@ -724,7 +724,7 @@ Parser.prototype.findSignature = function ( timestampNode, until ) {
|
||||||
// Handle links nested in formatting elements.
|
// Handle links nested in formatting elements.
|
||||||
if ( event === 'leave' && node.nodeType === Node.ELEMENT_NODE && node.tagName.toLowerCase() === 'a' ) {
|
if ( event === 'leave' && node.nodeType === Node.ELEMENT_NODE && node.tagName.toLowerCase() === 'a' ) {
|
||||||
if ( !node.classList.contains( 'ext-discussiontools-init-timestamplink' ) ) {
|
if ( !node.classList.contains( 'ext-discussiontools-init-timestamplink' ) ) {
|
||||||
var user = this.getUsernameFromLink( node );
|
const user = this.getUsernameFromLink( node );
|
||||||
if ( user ) {
|
if ( user ) {
|
||||||
// Accept the first link to the user namespace, then only accept links to that user
|
// Accept the first link to the user namespace, then only accept links to that user
|
||||||
if ( sigUsername === null ) {
|
if ( sigUsername === null ) {
|
||||||
|
@ -744,13 +744,13 @@ Parser.prototype.findSignature = function ( timestampNode, until ) {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
var range = {
|
const range = {
|
||||||
startContainer: lastLinkNode.parentNode,
|
startContainer: lastLinkNode.parentNode,
|
||||||
startOffset: utils.childIndexOf( lastLinkNode ),
|
startOffset: utils.childIndexOf( lastLinkNode ),
|
||||||
endContainer: timestampNode.parentNode,
|
endContainer: timestampNode.parentNode,
|
||||||
endOffset: utils.childIndexOf( timestampNode ) + 1
|
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.
|
// Expand the range so that it covers sibling nodes.
|
||||||
// This will include any wrapping formatting elements as part of the signature.
|
// This will include any wrapping formatting elements as part of the signature.
|
||||||
|
@ -761,7 +761,7 @@ Parser.prototype.findSignature = function ( timestampNode, until ) {
|
||||||
// "« Saper // dyskusja »"
|
// "« Saper // dyskusja »"
|
||||||
//
|
//
|
||||||
// TODO Not sure if this is actually good, might be better to just use the range...
|
// 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 {
|
return {
|
||||||
nodes: sigNodes,
|
nodes: sigNodes,
|
||||||
|
@ -784,9 +784,9 @@ Parser.prototype.findSignature = function ( timestampNode, until ) {
|
||||||
* @return {Node}
|
* @return {Node}
|
||||||
*/
|
*/
|
||||||
Parser.prototype.nextInterestingLeafNode = function ( 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,
|
rootNode,
|
||||||
// eslint-disable-next-line no-bitwise
|
// eslint-disable-next-line no-bitwise
|
||||||
NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_TEXT,
|
NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_TEXT,
|
||||||
|
@ -827,14 +827,14 @@ Parser.prototype.nextInterestingLeafNode = function ( node ) {
|
||||||
* @return {Object} Range-like object
|
* @return {Object} Range-like object
|
||||||
*/
|
*/
|
||||||
function adjustSigRange( sigNodes, match, node ) {
|
function adjustSigRange( sigNodes, match, node ) {
|
||||||
var firstSigNode = sigNodes[ sigNodes.length - 1 ];
|
const firstSigNode = sigNodes[ sigNodes.length - 1 ];
|
||||||
var lastSigNode = sigNodes[ 0 ];
|
const lastSigNode = sigNodes[ 0 ];
|
||||||
|
|
||||||
// TODO Document why this needs to be so complicated
|
// 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 :
|
match.matchData.index + match.matchData[ 0 ].length - match.offset :
|
||||||
utils.childIndexOf( lastSigNode ) + 1;
|
utils.childIndexOf( lastSigNode ) + 1;
|
||||||
var sigRange = {
|
const sigRange = {
|
||||||
startContainer: firstSigNode.parentNode,
|
startContainer: firstSigNode.parentNode,
|
||||||
startOffset: utils.childIndexOf( firstSigNode ),
|
startOffset: utils.childIndexOf( firstSigNode ),
|
||||||
endContainer: lastSigNode === node ? node : lastSigNode.parentNode,
|
endContainer: lastSigNode === node ? node : lastSigNode.parentNode,
|
||||||
|
@ -847,13 +847,13 @@ function adjustSigRange( sigNodes, match, node ) {
|
||||||
* @return {ThreadItemSet}
|
* @return {ThreadItemSet}
|
||||||
*/
|
*/
|
||||||
Parser.prototype.buildThreadItems = function () {
|
Parser.prototype.buildThreadItems = function () {
|
||||||
var result = new ThreadItemSet();
|
const result = new ThreadItemSet();
|
||||||
|
|
||||||
var
|
const
|
||||||
dfParsers = this.getLocalTimestampParsers(),
|
dfParsers = this.getLocalTimestampParsers(),
|
||||||
timestampRegexps = this.getLocalTimestampRegexps();
|
timestampRegexps = this.getLocalTimestampRegexps();
|
||||||
|
|
||||||
var treeWalker = this.rootNode.ownerDocument.createTreeWalker(
|
const treeWalker = this.rootNode.ownerDocument.createTreeWalker(
|
||||||
this.rootNode,
|
this.rootNode,
|
||||||
// eslint-disable-next-line no-bitwise
|
// eslint-disable-next-line no-bitwise
|
||||||
NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_TEXT,
|
NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_TEXT,
|
||||||
|
@ -861,14 +861,14 @@ Parser.prototype.buildThreadItems = function () {
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
|
|
||||||
var curComment, range;
|
let curComment, range;
|
||||||
var curCommentEnd = null;
|
let curCommentEnd = null;
|
||||||
|
|
||||||
var node;
|
let node;
|
||||||
while ( ( node = treeWalker.nextNode() ) ) {
|
while ( ( node = treeWalker.nextNode() ) ) {
|
||||||
var match;
|
var match;
|
||||||
if ( node.tagName && ( match = node.tagName.match( /^h([1-6])$/i ) ) ) {
|
if ( node.tagName && ( match = node.tagName.match( /^h([1-6])$/i ) ) ) {
|
||||||
var headingNode = utils.getHeadlineNode( node );
|
const headingNode = utils.getHeadlineNode( node );
|
||||||
range = {
|
range = {
|
||||||
startContainer: headingNode,
|
startContainer: headingNode,
|
||||||
startOffset: 0,
|
startOffset: 0,
|
||||||
|
@ -880,9 +880,9 @@ Parser.prototype.buildThreadItems = function () {
|
||||||
result.addThreadItem( curComment );
|
result.addThreadItem( curComment );
|
||||||
curCommentEnd = node;
|
curCommentEnd = node;
|
||||||
} else if ( node.nodeType === Node.TEXT_NODE && ( match = this.findTimestamp( node, timestampRegexps ) ) ) {
|
} else if ( node.nodeType === Node.TEXT_NODE && ( match = this.findTimestamp( node, timestampRegexps ) ) ) {
|
||||||
var warnings = [];
|
const warnings = [];
|
||||||
var foundSignature = this.findSignature( node, curCommentEnd );
|
const foundSignature = this.findSignature( node, curCommentEnd );
|
||||||
var author = foundSignature.username;
|
const author = foundSignature.username;
|
||||||
|
|
||||||
if ( !author ) {
|
if ( !author ) {
|
||||||
// Ignore timestamps for which we couldn't find a signature. It's probably not a real
|
// 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 );
|
timestampRanges.push( match.range );
|
||||||
|
|
||||||
// Everything from the last comment up to here is the next comment
|
// 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 ];
|
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
|
// 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,
|
endNode,
|
||||||
// eslint-disable-next-line no-loop-func
|
// eslint-disable-next-line no-loop-func
|
||||||
( event, n ) => {
|
( event, n ) => {
|
||||||
var match2, foundSignature2;
|
let match2, foundSignature2;
|
||||||
if ( utils.isBlockElement( n ) || utils.isCommentSeparator( n ) ) {
|
if ( utils.isBlockElement( n ) || utils.isCommentSeparator( n ) ) {
|
||||||
// Stop when entering or leaving a block node
|
// Stop when entering or leaving a block node
|
||||||
return true;
|
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.textContent.replace( /[\t\n\f\r ]+$/, '' ).length :
|
||||||
endNode.childNodes.length;
|
endNode.childNodes.length;
|
||||||
range = {
|
range = {
|
||||||
|
@ -950,19 +950,19 @@ Parser.prototype.buildThreadItems = function () {
|
||||||
endOffset: length
|
endOffset: length
|
||||||
};
|
};
|
||||||
|
|
||||||
var startLevel = utils.getIndentLevel( startNode, this.rootNode ) + 1;
|
const startLevel = utils.getIndentLevel( startNode, this.rootNode ) + 1;
|
||||||
var endLevel = utils.getIndentLevel( node, this.rootNode ) + 1;
|
const endLevel = utils.getIndentLevel( node, this.rootNode ) + 1;
|
||||||
if ( startLevel !== endLevel ) {
|
if ( startLevel !== endLevel ) {
|
||||||
warnings.push( 'Comment starts and ends with different indentation' );
|
warnings.push( 'Comment starts and ends with different indentation' );
|
||||||
}
|
}
|
||||||
// Should this use the indent level of `startNode` or `node`?
|
// 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 ) {
|
if ( !parserResult ) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
var dateTime = parserResult.date;
|
const dateTime = parserResult.date;
|
||||||
if ( parserResult.warning ) {
|
if ( parserResult.warning ) {
|
||||||
warnings.push( parserResult.warning );
|
warnings.push( parserResult.warning );
|
||||||
}
|
}
|
||||||
|
@ -989,7 +989,7 @@ Parser.prototype.buildThreadItems = function () {
|
||||||
endContainer: this.rootNode,
|
endContainer: this.rootNode,
|
||||||
endOffset: 0
|
endOffset: 0
|
||||||
};
|
};
|
||||||
var fakeHeading = new HeadingItem( range, null );
|
const fakeHeading = new HeadingItem( range, null );
|
||||||
fakeHeading.rootNode = this.rootNode;
|
fakeHeading.rootNode = this.rootNode;
|
||||||
result.addThreadItem( fakeHeading );
|
result.addThreadItem( fakeHeading );
|
||||||
}
|
}
|
||||||
|
@ -1019,7 +1019,7 @@ Parser.prototype.truncateForId = function ( text ) {
|
||||||
* @return {string}
|
* @return {string}
|
||||||
*/
|
*/
|
||||||
Parser.prototype.computeId = function ( threadItem, previousItems ) {
|
Parser.prototype.computeId = function ( threadItem, previousItems ) {
|
||||||
var id, headline;
|
let id, headline;
|
||||||
|
|
||||||
if ( threadItem instanceof HeadingItem && threadItem.placeholderHeading ) {
|
if ( threadItem instanceof HeadingItem && threadItem.placeholderHeading ) {
|
||||||
// The range points to the root note, using it like below results in silly values
|
// 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
|
// 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
|
// in one edit, or within a minute), append sequential numbers
|
||||||
var threadItemParent = threadItem.parent;
|
const threadItemParent = threadItem.parent;
|
||||||
if ( threadItemParent instanceof HeadingItem && !threadItemParent.placeholderHeading ) {
|
if ( threadItemParent instanceof HeadingItem && !threadItemParent.placeholderHeading ) {
|
||||||
headline = threadItemParent.range.startContainer;
|
headline = threadItemParent.range.startContainer;
|
||||||
id += '-' + this.truncateForId( headline.getAttribute( 'id' ) || '' );
|
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),
|
// (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
|
// include the oldest timestamp in the thread (i.e. date the thread was started) in the
|
||||||
// heading ID.
|
// heading ID.
|
||||||
var oldestComment = threadItem.getOldestReply();
|
const oldestComment = threadItem.getOldestReply();
|
||||||
if ( oldestComment ) {
|
if ( oldestComment ) {
|
||||||
id += '-' + oldestComment.getTimestampString();
|
id += '-' + oldestComment.getTimestampString();
|
||||||
}
|
}
|
||||||
|
@ -1058,7 +1058,7 @@ Parser.prototype.computeId = function ( threadItem, previousItems ) {
|
||||||
// Well, that's tough
|
// Well, that's tough
|
||||||
threadItem.warnings.push( 'Duplicate comment ID' );
|
threadItem.warnings.push( 'Duplicate comment ID' );
|
||||||
// Finally, disambiguate by adding sequential numbers, to allow replying to both comments
|
// Finally, disambiguate by adding sequential numbers, to allow replying to both comments
|
||||||
var number = 1;
|
let number = 1;
|
||||||
while ( previousItems.findCommentById( id + '-' + number ) ) {
|
while ( previousItems.findCommentById( id + '-' + number ) ) {
|
||||||
number++;
|
number++;
|
||||||
}
|
}
|
||||||
|
@ -1078,7 +1078,7 @@ Parser.prototype.computeId = function ( threadItem, previousItems ) {
|
||||||
* @return {string}
|
* @return {string}
|
||||||
*/
|
*/
|
||||||
Parser.prototype.computeName = function ( threadItem ) {
|
Parser.prototype.computeName = function ( threadItem ) {
|
||||||
var name, mainComment;
|
let name, mainComment;
|
||||||
|
|
||||||
if ( threadItem instanceof HeadingItem ) {
|
if ( threadItem instanceof HeadingItem ) {
|
||||||
name = 'h-';
|
name = 'h-';
|
||||||
|
@ -1102,10 +1102,10 @@ Parser.prototype.computeName = function ( threadItem ) {
|
||||||
* @param {ThreadItemSet} result
|
* @param {ThreadItemSet} result
|
||||||
*/
|
*/
|
||||||
Parser.prototype.buildThreads = function ( result ) {
|
Parser.prototype.buildThreads = function ( result ) {
|
||||||
var lastHeading = null;
|
let lastHeading = null;
|
||||||
var replies = [];
|
const replies = [];
|
||||||
|
|
||||||
var i, threadItem;
|
let i, threadItem;
|
||||||
for ( i = 0; i < result.threadItems.length; i++ ) {
|
for ( i = 0; i < result.threadItems.length; i++ ) {
|
||||||
threadItem = result.threadItems[ i ];
|
threadItem = result.threadItems[ i ];
|
||||||
|
|
||||||
|
@ -1122,7 +1122,7 @@ Parser.prototype.buildThreads = function ( result ) {
|
||||||
// New root (thread)
|
// New root (thread)
|
||||||
// Attach as a sub-thread to preceding higher-level heading.
|
// 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.
|
// 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 ) {
|
while ( maybeParent && maybeParent.headingLevel >= threadItem.headingLevel ) {
|
||||||
maybeParent = maybeParent.parent;
|
maybeParent = maybeParent.parent;
|
||||||
}
|
}
|
||||||
|
@ -1153,14 +1153,14 @@ Parser.prototype.buildThreads = function ( result ) {
|
||||||
* @param {ThreadItemSet} result
|
* @param {ThreadItemSet} result
|
||||||
*/
|
*/
|
||||||
Parser.prototype.computeIdsAndNames = function ( result ) {
|
Parser.prototype.computeIdsAndNames = function ( result ) {
|
||||||
var i, threadItem;
|
let i, threadItem;
|
||||||
for ( i = 0; i < result.threadItems.length; i++ ) {
|
for ( i = 0; i < result.threadItems.length; i++ ) {
|
||||||
threadItem = result.threadItems[ i ];
|
threadItem = result.threadItems[ i ];
|
||||||
|
|
||||||
var name = this.computeName( threadItem );
|
const name = this.computeName( threadItem );
|
||||||
threadItem.name = name;
|
threadItem.name = name;
|
||||||
|
|
||||||
var id = this.computeId( threadItem, result );
|
const id = this.computeId( threadItem, result );
|
||||||
threadItem.id = id;
|
threadItem.id = id;
|
||||||
|
|
||||||
result.updateIdAndNameMaps( threadItem );
|
result.updateIdAndNameMaps( threadItem );
|
||||||
|
@ -1172,17 +1172,17 @@ Parser.prototype.computeIdsAndNames = function ( result ) {
|
||||||
* @return {CommentItem|null}
|
* @return {CommentItem|null}
|
||||||
*/
|
*/
|
||||||
Parser.prototype.getThreadStartComment = function ( threadItem ) {
|
Parser.prototype.getThreadStartComment = function ( threadItem ) {
|
||||||
var oldest = null;
|
let oldest = null;
|
||||||
if ( threadItem instanceof CommentItem ) {
|
if ( threadItem instanceof CommentItem ) {
|
||||||
oldest = threadItem;
|
oldest = threadItem;
|
||||||
}
|
}
|
||||||
// Check all replies. This can't just use the first comment because threads are often summarized
|
// Check all replies. This can't just use the first comment because threads are often summarized
|
||||||
// at the top when the discussion is closed.
|
// at the top when the discussion is closed.
|
||||||
for ( var i = 0; i < threadItem.replies.length; i++ ) {
|
for ( let i = 0; i < threadItem.replies.length; i++ ) {
|
||||||
var comment = threadItem.replies[ i ];
|
const comment = threadItem.replies[ i ];
|
||||||
// Don't include sub-threads to avoid changing the ID when threads are "merged".
|
// Don't include sub-threads to avoid changing the ID when threads are "merged".
|
||||||
if ( comment instanceof CommentItem ) {
|
if ( comment instanceof CommentItem ) {
|
||||||
var oldestInReplies = this.getThreadStartComment( comment );
|
const oldestInReplies = this.getThreadStartComment( comment );
|
||||||
if ( !oldest || oldestInReplies.timestamp.isBefore( oldest.timestamp ) ) {
|
if ( !oldest || oldestInReplies.timestamp.isBefore( oldest.timestamp ) ) {
|
||||||
oldest = oldestInReplies;
|
oldest = oldestInReplies;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ var featuresEnabled = mw.config.get( 'wgDiscussionToolsFeaturesEnabled' ) || {};
|
||||||
|
|
||||||
function tryInfuse( $element ) {
|
function tryInfuse( $element ) {
|
||||||
if ( $element.length ) {
|
if ( $element.length ) {
|
||||||
var element = null;
|
let element = null;
|
||||||
// $.data() might have already been cleared by jQuery if the elements were removed, ignore
|
// $.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,
|
// TODO: We should keep references to the OO.ui.ButtonWidget objects instead of infusing again,
|
||||||
// which would avoid this issue too
|
// 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 = $pageContainer.find( '.ext-discussiontools-init-replylink-buttons[ data-mw-thread-id ]:not( :empty )' );
|
||||||
|
|
||||||
this.$replyLinkSets.each( ( i, replyLinkContainer ) => {
|
this.$replyLinkSets.each( ( i, replyLinkContainer ) => {
|
||||||
var replyButton = tryInfuse( $( replyLinkContainer ).find( '.ext-discussiontools-init-replybutton' ) );
|
const replyButton = tryInfuse( $( replyLinkContainer ).find( '.ext-discussiontools-init-replybutton' ) );
|
||||||
var $replyLink = $( replyLinkContainer ).find( '.ext-discussiontools-init-replylink-reply' );
|
const $replyLink = $( replyLinkContainer ).find( '.ext-discussiontools-init-replylink-reply' );
|
||||||
$replyLink.on( 'click keypress', this.onReplyLinkClickHandler );
|
$replyLink.on( 'click keypress', this.onReplyLinkClickHandler );
|
||||||
if ( replyButton ) {
|
if ( replyButton ) {
|
||||||
replyButton.on( 'click', this.onReplyButtonClickHandler, [ replyButton ] );
|
replyButton.on( 'click', this.onReplyButtonClickHandler, [ replyButton ] );
|
||||||
|
@ -49,7 +49,7 @@ function ReplyLinksController( $pageContainer ) {
|
||||||
// "Add topic" link in the skin interface
|
// "Add topic" link in the skin interface
|
||||||
if ( featuresEnabled.newtopictool ) {
|
if ( featuresEnabled.newtopictool ) {
|
||||||
// eslint-disable-next-line no-jquery/no-global-selector
|
// eslint-disable-next-line no-jquery/no-global-selector
|
||||||
var $addSectionTab = $( '#ca-addsection' );
|
const $addSectionTab = $( '#ca-addsection' );
|
||||||
if ( $addSectionTab.length ) {
|
if ( $addSectionTab.length ) {
|
||||||
this.$addSectionLink = $addSectionTab.find( 'a' );
|
this.$addSectionLink = $addSectionTab.find( 'a' );
|
||||||
this.$addSectionLink.on( 'click keypress', this.onAddSectionLinkClickHandler );
|
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
|
// Browser plugins (such as Google Translate) may add extra tags inside
|
||||||
// the link, so find the containing link tag with the data we need.
|
// 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 ) {
|
if ( !$linkSet.length ) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -93,7 +93,7 @@ ReplyLinksController.prototype.onReplyLinkClick = function ( e ) {
|
||||||
};
|
};
|
||||||
|
|
||||||
ReplyLinksController.prototype.onReplyButtonClick = function ( button ) {
|
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 );
|
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
|
// 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 ) {
|
if ( !href ) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var data = this.parseNewTopicLink( href );
|
const data = this.parseNewTopicLink( href );
|
||||||
if ( !data ) {
|
if ( !data ) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -142,9 +142,9 @@ ReplyLinksController.prototype.onAnyLinkClick = function ( e ) {
|
||||||
* @return {Object|null} `null` if not a new topic link, parameters otherwise
|
* @return {Object|null} `null` if not a new topic link, parameters otherwise
|
||||||
*/
|
*/
|
||||||
ReplyLinksController.prototype.parseNewTopicLink = function ( href ) {
|
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 ) {
|
if ( !title ) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -156,7 +156,7 @@ ReplyLinksController.prototype.parseNewTopicLink = function ( href ) {
|
||||||
title.getMainText().split( '/' )[ 0 ] === parserData.specialNewSectionName
|
title.getMainText().split( '/' )[ 0 ] === parserData.specialNewSectionName
|
||||||
) {
|
) {
|
||||||
// Get the real title from the subpage parameter
|
// 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 );
|
title = mw.Title.newFromText( param );
|
||||||
if ( !title ) {
|
if ( !title ) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -181,7 +181,7 @@ ReplyLinksController.prototype.parseNewTopicLink = function ( href ) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var data = {};
|
const data = {};
|
||||||
if ( searchParams.get( 'editintro' ) ) {
|
if ( searchParams.get( 'editintro' ) ) {
|
||||||
data.editintro = searchParams.get( 'editintro' );
|
data.editintro = searchParams.get( 'editintro' );
|
||||||
}
|
}
|
||||||
|
@ -221,7 +221,7 @@ ReplyLinksController.prototype.isActivationEvent = function ( e ) {
|
||||||
|
|
||||||
ReplyLinksController.prototype.focusLink = function ( $linkSet ) {
|
ReplyLinksController.prototype.focusLink = function ( $linkSet ) {
|
||||||
if ( $linkSet.is( this.$replyLinkSets ) ) {
|
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
|
// Focus whichever is visible, the link or the button
|
||||||
if ( button ) {
|
if ( button ) {
|
||||||
button.focus();
|
button.focus();
|
||||||
|
@ -233,8 +233,8 @@ ReplyLinksController.prototype.focusLink = function ( $linkSet ) {
|
||||||
ReplyLinksController.prototype.setActiveLink = function ( $linkSet ) {
|
ReplyLinksController.prototype.setActiveLink = function ( $linkSet ) {
|
||||||
this.$activeLink = $linkSet;
|
this.$activeLink = $linkSet;
|
||||||
|
|
||||||
var isNewTopic = false;
|
let isNewTopic = false;
|
||||||
var activeButton;
|
let activeButton;
|
||||||
if ( this.$activeLink.is( this.$replyLinkSets ) ) {
|
if ( this.$activeLink.is( this.$replyLinkSets ) ) {
|
||||||
this.$activeLink.addClass( 'ext-discussiontools-init-replylink-active' );
|
this.$activeLink.addClass( 'ext-discussiontools-init-replylink-active' );
|
||||||
activeButton = tryInfuse( this.$activeLink.find( '.ext-discussiontools-init-replybutton' ) );
|
activeButton = tryInfuse( this.$activeLink.find( '.ext-discussiontools-init-replybutton' ) );
|
||||||
|
@ -248,8 +248,8 @@ ReplyLinksController.prototype.setActiveLink = function ( $linkSet ) {
|
||||||
$( '#ca-view' ).removeClass( 'selected' );
|
$( '#ca-view' ).removeClass( 'selected' );
|
||||||
}
|
}
|
||||||
|
|
||||||
var title = mw.Title.newFromText( mw.config.get( 'wgRelevantPageName' ) );
|
const title = mw.Title.newFromText( mw.config.get( 'wgRelevantPageName' ) );
|
||||||
var pageTitleMsg = mw.message( 'pagetitle',
|
const pageTitleMsg = mw.message( 'pagetitle',
|
||||||
mw.msg(
|
mw.msg(
|
||||||
isNewTopic ?
|
isNewTopic ?
|
||||||
'discussiontools-pagetitle-newtopic' :
|
'discussiontools-pagetitle-newtopic' :
|
||||||
|
@ -269,8 +269,8 @@ ReplyLinksController.prototype.setActiveLink = function ( $linkSet ) {
|
||||||
|
|
||||||
$( document.body ).addClass( 'ext-discussiontools-init-replylink-open' );
|
$( document.body ).addClass( 'ext-discussiontools-init-replylink-open' );
|
||||||
this.$replyLinkSets.each( ( i, replyLinkContainer ) => {
|
this.$replyLinkSets.each( ( i, replyLinkContainer ) => {
|
||||||
var replyButton = tryInfuse( $( replyLinkContainer ).find( '.ext-discussiontools-init-replybutton' ) );
|
const replyButton = tryInfuse( $( replyLinkContainer ).find( '.ext-discussiontools-init-replybutton' ) );
|
||||||
var $replyLink = $( replyLinkContainer ).find( '.ext-discussiontools-init-replylink-reply' );
|
const $replyLink = $( replyLinkContainer ).find( '.ext-discussiontools-init-replylink-reply' );
|
||||||
$replyLink.attr( 'tabindex', -1 );
|
$replyLink.attr( 'tabindex', -1 );
|
||||||
if ( !replyButton ) {
|
if ( !replyButton ) {
|
||||||
return;
|
return;
|
||||||
|
@ -289,7 +289,7 @@ ReplyLinksController.prototype.setActiveLink = function ( $linkSet ) {
|
||||||
};
|
};
|
||||||
|
|
||||||
ReplyLinksController.prototype.clearActiveLink = function () {
|
ReplyLinksController.prototype.clearActiveLink = function () {
|
||||||
var activeButton;
|
let activeButton;
|
||||||
if ( this.$activeLink.is( this.$replyLinkSets ) ) {
|
if ( this.$activeLink.is( this.$replyLinkSets ) ) {
|
||||||
this.$activeLink.removeClass( 'ext-discussiontools-init-replylink-active' );
|
this.$activeLink.removeClass( 'ext-discussiontools-init-replylink-active' );
|
||||||
activeButton = tryInfuse( this.$activeLink.find( '.ext-discussiontools-init-replybutton' ) );
|
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' );
|
$( document.body ).removeClass( 'ext-discussiontools-init-replylink-open' );
|
||||||
this.$replyLinkSets.each( ( i, replyLinkContainer ) => {
|
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 );
|
$replyLink.attr( 'tabindex', 0 );
|
||||||
var replyButton = tryInfuse( $( replyLinkContainer ).find( '.ext-discussiontools-init-replybutton' ) );
|
const replyButton = tryInfuse( $( replyLinkContainer ).find( '.ext-discussiontools-init-replybutton' ) );
|
||||||
if ( !replyButton ) {
|
if ( !replyButton ) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -336,11 +336,11 @@ ReplyLinksController.prototype.teardown = function () {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.$replyLinkSets.each( ( i, replyLinkContainer ) => {
|
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 ) {
|
if ( replyButton ) {
|
||||||
replyButton.off( 'click', this.onReplyButtonClickHandler );
|
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 );
|
$replyLink.off( 'click keypress', this.onReplyLinkClickHandler );
|
||||||
} );
|
} );
|
||||||
|
|
||||||
|
|
|
@ -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.
|
// by an older version of our PHP code. Code below must be able to handle that.
|
||||||
// See ThreadItem::jsonSerialize() in PHP.
|
// 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 ) {
|
switch ( hash.type ) {
|
||||||
case 'comment':
|
case 'comment':
|
||||||
// Late require to avoid circular dependency
|
// Late require to avoid circular dependency
|
||||||
|
@ -102,9 +102,9 @@ ThreadItem.static.newFromJSON = function ( json, rootNode ) {
|
||||||
|
|
||||||
item.rootNode = rootNode;
|
item.rootNode = rootNode;
|
||||||
|
|
||||||
var idEscaped = $.escapeSelector( item.id );
|
const idEscaped = $.escapeSelector( item.id );
|
||||||
var startMarker = document.getElementById( item.id );
|
const startMarker = document.getElementById( item.id );
|
||||||
var endMarker = document.querySelector( '[data-mw-comment-end="' + idEscaped + '"]' );
|
const endMarker = document.querySelector( '[data-mw-comment-end="' + idEscaped + '"]' );
|
||||||
|
|
||||||
item.range = {
|
item.range = {
|
||||||
// Start range after startMarker, because it produces funny results from getBoundingClientRect
|
// Start range after startMarker, because it produces funny results from getBoundingClientRect
|
||||||
|
@ -125,10 +125,10 @@ ThreadItem.prototype.calculateThreadSummary = function () {
|
||||||
if ( this.authors ) {
|
if ( this.authors ) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var authors = {};
|
const authors = {};
|
||||||
var commentCount = 0;
|
let commentCount = 0;
|
||||||
var oldestReply = null;
|
let oldestReply = null;
|
||||||
var latestReply = null;
|
let latestReply = null;
|
||||||
function threadScan( comment ) {
|
function threadScan( comment ) {
|
||||||
if ( comment.type === 'comment' ) {
|
if ( comment.type === 'comment' ) {
|
||||||
authors[ comment.author ] = authors[ comment.author ] || {
|
authors[ comment.author ] = authors[ comment.author ] || {
|
||||||
|
@ -214,7 +214,7 @@ ThreadItem.prototype.getOldestReply = function () {
|
||||||
* @return {ThreadItem[]} Thread items
|
* @return {ThreadItem[]} Thread items
|
||||||
*/
|
*/
|
||||||
ThreadItem.prototype.getThreadItemsBelow = function () {
|
ThreadItem.prototype.getThreadItemsBelow = function () {
|
||||||
var threadItems = [];
|
const threadItems = [];
|
||||||
function getReplies( comment ) {
|
function getReplies( comment ) {
|
||||||
threadItems.push( comment );
|
threadItems.push( comment );
|
||||||
comment.replies.forEach( getReplies );
|
comment.replies.forEach( getReplies );
|
||||||
|
@ -231,8 +231,8 @@ ThreadItem.prototype.getThreadItemsBelow = function () {
|
||||||
* @return {Range}
|
* @return {Range}
|
||||||
*/
|
*/
|
||||||
ThreadItem.prototype.getRange = function () {
|
ThreadItem.prototype.getRange = function () {
|
||||||
var doc = this.range.startContainer.ownerDocument;
|
const doc = this.range.startContainer.ownerDocument;
|
||||||
var nativeRange = doc.createRange();
|
const nativeRange = doc.createRange();
|
||||||
nativeRange.setStart( this.range.startContainer, this.range.startOffset );
|
nativeRange.setStart( this.range.startContainer, this.range.startOffset );
|
||||||
nativeRange.setEnd( this.range.endContainer, this.range.endOffset );
|
nativeRange.setEnd( this.range.endContainer, this.range.endOffset );
|
||||||
return nativeRange;
|
return nativeRange;
|
||||||
|
|
|
@ -26,10 +26,10 @@ OO.initClass( ThreadItemSet );
|
||||||
* @return {ThreadItemSet}
|
* @return {ThreadItemSet}
|
||||||
*/
|
*/
|
||||||
ThreadItemSet.static.newFromJSON = function ( threads, rootNode, parser ) {
|
ThreadItemSet.static.newFromJSON = function ( threads, rootNode, parser ) {
|
||||||
var result = new ThreadItemSet();
|
const result = new ThreadItemSet();
|
||||||
|
|
||||||
function infuse( itemHash, parent ) {
|
function infuse( itemHash, parent ) {
|
||||||
var item = ThreadItem.static.newFromJSON( itemHash, rootNode );
|
const item = ThreadItem.static.newFromJSON( itemHash, rootNode );
|
||||||
|
|
||||||
result.addThreadItem( item );
|
result.addThreadItem( item );
|
||||||
|
|
||||||
|
|
|
@ -56,7 +56,7 @@ function getApi() {
|
||||||
* @return {jQuery.Promise}
|
* @return {jQuery.Promise}
|
||||||
*/
|
*/
|
||||||
function getPageData( pageName, oldId, apiParams ) {
|
function getPageData( pageName, oldId, apiParams ) {
|
||||||
var api = getApi();
|
const api = getApi();
|
||||||
apiParams = apiParams || {};
|
apiParams = apiParams || {};
|
||||||
|
|
||||||
pageDataCache[ pageName ] = pageDataCache[ pageName ] || {};
|
pageDataCache[ pageName ] = pageDataCache[ pageName ] || {};
|
||||||
|
@ -64,7 +64,7 @@ function getPageData( pageName, oldId, apiParams ) {
|
||||||
return pageDataCache[ pageName ][ oldId ];
|
return pageDataCache[ pageName ][ oldId ];
|
||||||
}
|
}
|
||||||
|
|
||||||
var lintPromise, transcludedFromPromise;
|
let lintPromise, transcludedFromPromise;
|
||||||
if ( oldId ) {
|
if ( oldId ) {
|
||||||
lintPromise = api.get( {
|
lintPromise = api.get( {
|
||||||
action: 'query',
|
action: 'query',
|
||||||
|
@ -84,13 +84,13 @@ function getPageData( pageName, oldId, apiParams ) {
|
||||||
transcludedFromPromise = $.Deferred().resolve( {} ).promise();
|
transcludedFromPromise = $.Deferred().resolve( {} ).promise();
|
||||||
}
|
}
|
||||||
|
|
||||||
var veMetadataPromise = api.get( Object.assign( {
|
const veMetadataPromise = api.get( Object.assign( {
|
||||||
action: 'visualeditor',
|
action: 'visualeditor',
|
||||||
paction: 'metadata',
|
paction: 'metadata',
|
||||||
page: pageName
|
page: pageName
|
||||||
}, apiParams ) ).then( ( response ) => OO.getProp( response, 'visualeditor' ) || [] );
|
}, apiParams ) ).then( ( response ) => OO.getProp( response, 'visualeditor' ) || [] );
|
||||||
|
|
||||||
var promise = $.when( lintPromise, transcludedFromPromise, veMetadataPromise )
|
const promise = $.when( lintPromise, transcludedFromPromise, veMetadataPromise )
|
||||||
.then( ( linterrors, transcludedfrom, metadata ) => ( {
|
.then( ( linterrors, transcludedfrom, metadata ) => ( {
|
||||||
linterrors: linterrors,
|
linterrors: linterrors,
|
||||||
transcludedfrom: transcludedfrom,
|
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.
|
* Rejects with error data if the comment is transcluded, or there are lint errors on the page.
|
||||||
*/
|
*/
|
||||||
function checkThreadItemOnPage( pageName, oldId, threadItem ) {
|
function checkThreadItemOnPage( pageName, oldId, threadItem ) {
|
||||||
var isNewTopic = threadItem.id === utils.NEW_TOPIC_COMMENT_ID;
|
const isNewTopic = threadItem.id === utils.NEW_TOPIC_COMMENT_ID;
|
||||||
var defaultMode = mw.user.options.get( 'discussiontools-editmode' ) || mw.config.get( 'wgDiscussionToolsFallbackEditMode' );
|
const defaultMode = mw.user.options.get( 'discussiontools-editmode' ) || mw.config.get( 'wgDiscussionToolsFallbackEditMode' );
|
||||||
var apiParams = null;
|
let apiParams = null;
|
||||||
if ( isNewTopic ) {
|
if ( isNewTopic ) {
|
||||||
apiParams = {
|
apiParams = {
|
||||||
section: 'new',
|
section: 'new',
|
||||||
|
@ -134,7 +134,7 @@ function checkThreadItemOnPage( pageName, oldId, threadItem ) {
|
||||||
|
|
||||||
return getPageData( pageName, oldId, apiParams )
|
return getPageData( pageName, oldId, apiParams )
|
||||||
.then( ( response ) => {
|
.then( ( response ) => {
|
||||||
var metadata = response.metadata,
|
const metadata = response.metadata,
|
||||||
lintErrors = response.linterrors,
|
lintErrors = response.linterrors,
|
||||||
transcludedFrom = response.transcludedfrom;
|
transcludedFrom = response.transcludedfrom;
|
||||||
|
|
||||||
|
@ -144,7 +144,7 @@ function checkThreadItemOnPage( pageName, oldId, threadItem ) {
|
||||||
// or if a thread item's parent changes.
|
// 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
|
// 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.
|
// 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 ) {
|
if ( isTranscludedFrom === undefined ) {
|
||||||
isTranscludedFrom = transcludedFrom[ threadItem.name ];
|
isTranscludedFrom = transcludedFrom[ threadItem.name ];
|
||||||
}
|
}
|
||||||
|
@ -158,11 +158,11 @@ function checkThreadItemOnPage( pageName, oldId, threadItem ) {
|
||||||
mw.message( 'discussiontools-error-comment-disappeared-reload' ).parse()
|
mw.message( 'discussiontools-error-comment-disappeared-reload' ).parse()
|
||||||
} ] } ).promise();
|
} ] } ).promise();
|
||||||
} else if ( isTranscludedFrom ) {
|
} 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
|
// 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 ) {
|
if ( follow ) {
|
||||||
transcludedErrMsg = mw.message(
|
transcludedErrMsg = mw.message(
|
||||||
'discussiontools-error-comment-is-transcluded-title',
|
'discussiontools-error-comment-is-transcluded-title',
|
||||||
|
@ -192,7 +192,7 @@ function checkThreadItemOnPage( pageName, oldId, threadItem ) {
|
||||||
|
|
||||||
if ( lintErrors.length ) {
|
if ( lintErrors.length ) {
|
||||||
// We currently only request the first error
|
// We currently only request the first error
|
||||||
var lintType = lintErrors[ 0 ].category;
|
const lintType = lintErrors[ 0 ].category;
|
||||||
|
|
||||||
return $.Deferred().reject( 'lint', { errors: [ {
|
return $.Deferred().reject( 'lint', { errors: [ {
|
||||||
code: 'lint',
|
code: 'lint',
|
||||||
|
@ -227,7 +227,7 @@ function getCheckboxesPromise( pageName, oldId ) {
|
||||||
pageName,
|
pageName,
|
||||||
oldId
|
oldId
|
||||||
).then( ( pageData ) => {
|
).then( ( pageData ) => {
|
||||||
var data = pageData.metadata,
|
const data = pageData.metadata,
|
||||||
checkboxesDef = {};
|
checkboxesDef = {};
|
||||||
|
|
||||||
mw.messages.set( data.checkboxesMessages );
|
mw.messages.set( data.checkboxesMessages );
|
||||||
|
@ -251,7 +251,7 @@ function getCheckboxesPromise( pageName, oldId ) {
|
||||||
* @return {string[]}
|
* @return {string[]}
|
||||||
*/
|
*/
|
||||||
function getReplyWidgetModules() {
|
function getReplyWidgetModules() {
|
||||||
var veConf = mw.config.get( 'wgVisualEditorConfig' ),
|
let veConf = mw.config.get( 'wgVisualEditorConfig' ),
|
||||||
modules = [ 'ext.discussionTools.ReplyWidget' ]
|
modules = [ 'ext.discussionTools.ReplyWidget' ]
|
||||||
.concat( veConf.pluginModules.filter( mw.loader.getState ) );
|
.concat( veConf.pluginModules.filter( mw.loader.getState ) );
|
||||||
|
|
||||||
|
@ -279,7 +279,7 @@ function getReplyWidgetModules() {
|
||||||
* @param {boolean} [state.tempUserCreated] Whether a temp user was just created
|
* @param {boolean} [state.tempUserCreated] Whether a temp user was just created
|
||||||
*/
|
*/
|
||||||
function init( $container, state ) {
|
function init( $container, state ) {
|
||||||
var
|
let
|
||||||
activeCommentId = null,
|
activeCommentId = null,
|
||||||
activeController = null,
|
activeController = null,
|
||||||
// Loads later to avoid circular dependency
|
// 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 );
|
pageThreads = ThreadItemSet.static.newFromJSON( mw.config.get( 'wgDiscussionToolsPageThreads' ) || [], $pageContainer[ 0 ], parser );
|
||||||
|
|
||||||
if ( featuresEnabled.topicsubscription ) {
|
if ( featuresEnabled.topicsubscription ) {
|
||||||
|
@ -350,7 +350,7 @@ function init( $container, state ) {
|
||||||
* @param {MemoryStorage} [storage] Storage object for autosave
|
* @param {MemoryStorage} [storage] Storage object for autosave
|
||||||
*/
|
*/
|
||||||
function setupController( comment, $link, mode, hideErrors, suppressNotifications, storage ) {
|
function setupController( comment, $link, mode, hideErrors, suppressNotifications, storage ) {
|
||||||
var commentController, $addSectionLink;
|
let commentController, $addSectionLink;
|
||||||
|
|
||||||
if ( !storage ) {
|
if ( !storage ) {
|
||||||
storage = new MemoryStorage( mw.storage, 'mw-ext-DiscussionTools-reply/' + comment.id, STORAGE_EXPIRY );
|
storage = new MemoryStorage( mw.storage, 'mw-ext-DiscussionTools-reply/' + comment.id, STORAGE_EXPIRY );
|
||||||
|
@ -404,7 +404,7 @@ function init( $container, state ) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function newTopicComment( data ) {
|
function newTopicComment( data ) {
|
||||||
var comment = new HeadingItem( {}, 2 );
|
const comment = new HeadingItem( {}, 2 );
|
||||||
comment.id = utils.NEW_TOPIC_COMMENT_ID;
|
comment.id = utils.NEW_TOPIC_COMMENT_ID;
|
||||||
comment.isNewTopic = true;
|
comment.isNewTopic = true;
|
||||||
Object.assign( comment, data );
|
Object.assign( comment, data );
|
||||||
|
@ -424,7 +424,7 @@ function init( $container, state ) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var teardownPromise = $.Deferred().resolve();
|
let teardownPromise = $.Deferred().resolve();
|
||||||
if ( commentId === utils.NEW_TOPIC_COMMENT_ID ) {
|
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 this is a new topic link, and a reply widget is open, attempt to close it first.
|
||||||
if ( activeController ) {
|
if ( activeController ) {
|
||||||
|
@ -447,7 +447,7 @@ function init( $container, state ) {
|
||||||
if ( activeController ) {
|
if ( activeController ) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var comment;
|
let comment;
|
||||||
if ( commentId !== utils.NEW_TOPIC_COMMENT_ID ) {
|
if ( commentId !== utils.NEW_TOPIC_COMMENT_ID ) {
|
||||||
comment = pageThreads.findCommentById( commentId );
|
comment = pageThreads.findCommentById( commentId );
|
||||||
} else {
|
} 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' ) :
|
mw.loader.using( 'mobile.init' ) :
|
||||||
$.Deferred().resolve().promise();
|
$.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
|
// Page-level handlers only need to be setup once
|
||||||
if ( !pageHandlersSetup ) {
|
if ( !pageHandlersSetup ) {
|
||||||
$( window ).on( 'popstate', () => {
|
$( window ).on( 'popstate', () => {
|
||||||
|
@ -556,9 +556,9 @@ function init( $container, state ) {
|
||||||
}
|
}
|
||||||
if ( state.firstLoad ) {
|
if ( state.firstLoad ) {
|
||||||
mobilePromise.then( () => {
|
mobilePromise.then( () => {
|
||||||
var findCommentQuery;
|
let findCommentQuery;
|
||||||
var isHeading = false;
|
let isHeading = false;
|
||||||
var highlightResult = highlighter.highlightTargetComment( pageThreads );
|
const highlightResult = highlighter.highlightTargetComment( pageThreads );
|
||||||
|
|
||||||
// Hash contains a non-replaced space (should be underscore), maybe due to
|
// Hash contains a non-replaced space (should be underscore), maybe due to
|
||||||
// manual creation or a broken third party tool. Just replace the spaces
|
// 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
|
// element, but the fixed hash does, to avoid affects on other apps which
|
||||||
// may use fragments with spaces.
|
// may use fragments with spaces.
|
||||||
if ( location.hash && !mw.util.getTargetFromFragment() && location.hash.indexOf( '%20' ) !== -1 ) {
|
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 ) ) {
|
if ( mw.util.getTargetFromFragment( fixedHash ) ) {
|
||||||
location.hash = fixedHash;
|
location.hash = fixedHash;
|
||||||
}
|
}
|
||||||
|
@ -580,8 +580,8 @@ function init( $container, state ) {
|
||||||
// Not a DT comment
|
// Not a DT comment
|
||||||
highlightResult.highlighted.length === 0 && highlightResult.requested.length === 0
|
highlightResult.highlighted.length === 0 && highlightResult.requested.length === 0
|
||||||
) {
|
) {
|
||||||
var fragment = location.hash.slice( 1 );
|
const fragment = location.hash.slice( 1 );
|
||||||
var ignorePatterns = [
|
const ignorePatterns = [
|
||||||
// A leading '/' or '!/' usually means a application route, e.g. /media, or /editor.
|
// 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
|
// We can't rule out a heading title (T349498), but they are unlikely
|
||||||
/^!?\//,
|
/^!?\//,
|
||||||
|
@ -611,8 +611,8 @@ function init( $container, state ) {
|
||||||
}
|
}
|
||||||
if ( findCommentQuery ) {
|
if ( findCommentQuery ) {
|
||||||
// TODO: Support multiple commentIds being requested and not all being found
|
// TODO: Support multiple commentIds being requested and not all being found
|
||||||
var dtConf = require( './config.json' );
|
const dtConf = require( './config.json' );
|
||||||
var findCommentRequest = dtConf.enablePermalinksFrontend ?
|
const findCommentRequest = dtConf.enablePermalinksFrontend ?
|
||||||
getApi().get( Object.assign( {
|
getApi().get( Object.assign( {
|
||||||
action: 'discussiontoolsfindcomment'
|
action: 'discussiontoolsfindcomment'
|
||||||
}, findCommentQuery ) ) :
|
}, findCommentQuery ) ) :
|
||||||
|
@ -621,14 +621,14 @@ function init( $container, state ) {
|
||||||
findCommentRequest,
|
findCommentRequest,
|
||||||
mw.loader.using( 'mediawiki.notification' )
|
mw.loader.using( 'mediawiki.notification' )
|
||||||
).then( ( results ) => {
|
).then( ( results ) => {
|
||||||
var result = results[ 0 ];
|
const result = results[ 0 ];
|
||||||
var titles = [];
|
let titles = [];
|
||||||
if ( result.discussiontoolsfindcomment ) {
|
if ( result.discussiontoolsfindcomment ) {
|
||||||
titles = result.discussiontoolsfindcomment.map( ( threadItemData ) => {
|
titles = result.discussiontoolsfindcomment.map( ( threadItemData ) => {
|
||||||
// Only show items that appear on the current revision of their page
|
// Only show items that appear on the current revision of their page
|
||||||
// and are not transcluded from another page
|
// and are not transcluded from another page
|
||||||
if ( threadItemData.couldredirect ) {
|
if ( threadItemData.couldredirect ) {
|
||||||
var title = mw.Title.newFromText(
|
const title = mw.Title.newFromText(
|
||||||
threadItemData.title + '#' +
|
threadItemData.title + '#' +
|
||||||
mw.util.escapeIdForLink( threadItemData.id )
|
mw.util.escapeIdForLink( threadItemData.id )
|
||||||
);
|
);
|
||||||
|
@ -638,8 +638,8 @@ function init( $container, state ) {
|
||||||
} ).filter( ( url ) => url );
|
} ).filter( ( url ) => url );
|
||||||
}
|
}
|
||||||
if ( titles.length ) {
|
if ( titles.length ) {
|
||||||
var $list = $( '<ul>' );
|
const $list = $( '<ul>' );
|
||||||
var $notification = $( '<div>' ).append(
|
const $notification = $( '<div>' ).append(
|
||||||
$( '<p>' ).text( mw.message(
|
$( '<p>' ).text( mw.message(
|
||||||
isHeading ?
|
isHeading ?
|
||||||
'discussiontools-target-heading-found-moved' :
|
'discussiontools-target-heading-found-moved' :
|
||||||
|
@ -696,7 +696,7 @@ function updatePageContents( $container, data ) {
|
||||||
|
|
||||||
// eslint-disable-next-line no-jquery/no-global-selector
|
// eslint-disable-next-line no-jquery/no-global-selector
|
||||||
if ( $( '#catlinks' ).length ) {
|
if ( $( '#catlinks' ).length ) {
|
||||||
var $categories = $( $.parseHTML( data.parse.categorieshtml ) );
|
const $categories = $( $.parseHTML( data.parse.categorieshtml ) );
|
||||||
mw.hook( 'wikipage.categories' ).fire( $categories );
|
mw.hook( 'wikipage.categories' ).fire( $categories );
|
||||||
// eslint-disable-next-line no-jquery/no-global-selector
|
// eslint-disable-next-line no-jquery/no-global-selector
|
||||||
$( '#catlinks' ).replaceWith( $categories );
|
$( '#catlinks' ).replaceWith( $categories );
|
||||||
|
@ -732,23 +732,23 @@ function updatePageContents( $container, data ) {
|
||||||
// TODO: Upstream this to core/skins, triggered by a hook (wikipage.content?)
|
// TODO: Upstream this to core/skins, triggered by a hook (wikipage.content?)
|
||||||
// eslint-disable-next-line no-jquery/no-global-selector
|
// eslint-disable-next-line no-jquery/no-global-selector
|
||||||
$( '#t-permalink' ).add( '#coll-download-as-rl' ).find( 'a' ).each( ( i, link ) => {
|
$( '#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 );
|
permalinkUrl.searchParams.set( 'oldid', data.parse.revid );
|
||||||
$( link ).attr( 'href', permalinkUrl.toString() );
|
$( link ).attr( 'href', permalinkUrl.toString() );
|
||||||
} );
|
} );
|
||||||
|
|
||||||
var url = new URL( location.href );
|
const url = new URL( location.href );
|
||||||
url.searchParams.delete( 'oldid' );
|
url.searchParams.delete( 'oldid' );
|
||||||
|
|
||||||
// If there are any other query parameters left, re-use that URL object.
|
// If there are any other query parameters left, re-use that URL object.
|
||||||
// Otherwise use the canonical style view url (T44553, T102363).
|
// Otherwise use the canonical style view url (T44553, T102363).
|
||||||
var keys = [];
|
const keys = [];
|
||||||
url.searchParams.forEach( ( val, key ) => {
|
url.searchParams.forEach( ( val, key ) => {
|
||||||
keys.push( key );
|
keys.push( key );
|
||||||
} );
|
} );
|
||||||
|
|
||||||
if ( !keys.length || ( keys.length === 1 && keys[ 0 ] === 'title' ) ) {
|
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;
|
viewUrl.hash = location.hash;
|
||||||
history.pushState( null, '', viewUrl );
|
history.pushState( null, '', viewUrl );
|
||||||
} else {
|
} else {
|
||||||
|
@ -809,7 +809,7 @@ function update( data, threadItem, pageName, replyWidget ) {
|
||||||
replyWidget.unbindBeforeUnloadHandler();
|
replyWidget.unbindBeforeUnloadHandler();
|
||||||
replyWidget.clearStorage();
|
replyWidget.clearStorage();
|
||||||
replyWidget.setPending( true );
|
replyWidget.setPending( true );
|
||||||
var params = { dtrepliedto: threadItem.id };
|
const params = { dtrepliedto: threadItem.id };
|
||||||
if ( data.tempusercreated ) {
|
if ( data.tempusercreated ) {
|
||||||
params.dttempusercreated = '1';
|
params.dttempusercreated = '1';
|
||||||
}
|
}
|
||||||
|
@ -826,7 +826,7 @@ function update( data, threadItem, pageName, replyWidget ) {
|
||||||
mw.dt.initState.tempUserCreated = data.tempusercreated;
|
mw.dt.initState.tempUserCreated = data.tempusercreated;
|
||||||
|
|
||||||
// Update page state
|
// Update page state
|
||||||
var pageUpdated = $.Deferred();
|
const pageUpdated = $.Deferred();
|
||||||
if ( pageName === mw.config.get( 'wgRelevantPageName' ) ) {
|
if ( pageName === mw.config.get( 'wgRelevantPageName' ) ) {
|
||||||
// We can use the result from the VisualEditor API
|
// We can use the result from the VisualEditor API
|
||||||
updatePageContents( $pageContainer, {
|
updatePageContents( $pageContainer, {
|
||||||
|
@ -854,7 +854,7 @@ function update( data, threadItem, pageName, replyWidget ) {
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// We saved to another page, we must purge and then fetch the current page
|
// We saved to another page, we must purge and then fetch the current page
|
||||||
var api = getApi();
|
const api = getApi();
|
||||||
api.post( {
|
api.post( {
|
||||||
action: 'purge',
|
action: 'purge',
|
||||||
titles: mw.config.get( 'wgRelevantPageName' )
|
titles: mw.config.get( 'wgRelevantPageName' )
|
||||||
|
@ -871,7 +871,7 @@ function update( data, threadItem, pageName, replyWidget ) {
|
||||||
|
|
||||||
// User logged in if module loaded.
|
// User logged in if module loaded.
|
||||||
if ( mw.loader.getState( 'mediawiki.page.watch.ajax' ) === 'ready' ) {
|
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(
|
watch.updateWatchLink(
|
||||||
mw.Title.newFromText( pageName ),
|
mw.Title.newFromText( pageName ),
|
||||||
|
|
|
@ -6,24 +6,24 @@ var updaters = [];
|
||||||
var isRtl = $( 'html' ).attr( 'dir' ) === 'rtl';
|
var isRtl = $( 'html' ).attr( 'dir' ) === 'rtl';
|
||||||
|
|
||||||
function markTimestamp( parser, node, match ) {
|
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 );
|
newNode.splitText( match.matchData[ 0 ].length );
|
||||||
|
|
||||||
var wrapper = document.createElement( 'span' );
|
const wrapper = document.createElement( 'span' );
|
||||||
wrapper.className = 'ext-discussiontools-debughighlighter-timestamp';
|
wrapper.className = 'ext-discussiontools-debughighlighter-timestamp';
|
||||||
// We might need to actually port all the date formatting code from MediaWiki's PHP code
|
// 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
|
// if we want to support displaying dates in all the formats available in user preferences
|
||||||
// (which include formats in several non-Gregorian calendars).
|
// (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.title = date.format() + ' / ' + date.fromNow();
|
||||||
wrapper.appendChild( newNode );
|
wrapper.appendChild( newNode );
|
||||||
node.parentNode.insertBefore( wrapper, node.nextSibling );
|
node.parentNode.insertBefore( wrapper, node.nextSibling );
|
||||||
}
|
}
|
||||||
|
|
||||||
function markSignature( sigNodes ) {
|
function markSignature( sigNodes ) {
|
||||||
var
|
const
|
||||||
where = sigNodes[ 0 ],
|
where = sigNodes[ 0 ],
|
||||||
wrapper = document.createElement( 'span' );
|
wrapper = document.createElement( 'span' );
|
||||||
wrapper.className = 'ext-discussiontools-debughighlighter-signature';
|
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"
|
// 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.
|
// 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' ) {
|
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 node.getBoundingClientRect();
|
||||||
}
|
}
|
||||||
return rect;
|
return rect;
|
||||||
|
@ -45,9 +45,9 @@ function fixFakeFirstHeadingRect( rect, comment ) {
|
||||||
|
|
||||||
function calculateSizes() {
|
function calculateSizes() {
|
||||||
// eslint-disable-next-line no-jquery/no-global-selector
|
// eslint-disable-next-line no-jquery/no-global-selector
|
||||||
var $content = $( '#mw-content-text' );
|
const $content = $( '#mw-content-text' );
|
||||||
var $test = $( '<dd>' ).appendTo( $( '<dl>' ).appendTo( $content ) );
|
const $test = $( '<dd>' ).appendTo( $( '<dl>' ).appendTo( $content ) );
|
||||||
var rect = $content[ 0 ].getBoundingClientRect();
|
const rect = $content[ 0 ].getBoundingClientRect();
|
||||||
|
|
||||||
initialOffset = isRtl ? document.body.scrollWidth - rect.left - rect.width : rect.left;
|
initialOffset = isRtl ? document.body.scrollWidth - rect.left - rect.width : rect.left;
|
||||||
indentWidth = parseFloat( $test.css( isRtl ? 'margin-right' : 'margin-left' ) ) +
|
indentWidth = parseFloat( $test.css( isRtl ? 'margin-right' : 'margin-left' ) ) +
|
||||||
|
@ -57,33 +57,33 @@ function calculateSizes() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function markComment( comment ) {
|
function markComment( comment ) {
|
||||||
var marker = document.createElement( 'div' );
|
const marker = document.createElement( 'div' );
|
||||||
marker.className = 'ext-discussiontools-debughighlighter-comment';
|
marker.className = 'ext-discussiontools-debughighlighter-comment';
|
||||||
|
|
||||||
if ( !firstMarker ) {
|
if ( !firstMarker ) {
|
||||||
firstMarker = marker;
|
firstMarker = marker;
|
||||||
}
|
}
|
||||||
|
|
||||||
var marker2 = null;
|
let marker2 = null;
|
||||||
if ( comment.parent ) {
|
if ( comment.parent ) {
|
||||||
marker2 = document.createElement( 'div' );
|
marker2 = document.createElement( 'div' );
|
||||||
marker2.className = 'ext-discussiontools-debughighlighter-comment-ruler';
|
marker2.className = 'ext-discussiontools-debughighlighter-comment-ruler';
|
||||||
}
|
}
|
||||||
|
|
||||||
var markerWarnings = null;
|
let markerWarnings = null;
|
||||||
if ( comment.warnings && comment.warnings.length ) {
|
if ( comment.warnings && comment.warnings.length ) {
|
||||||
markerWarnings = document.createElement( 'div' );
|
markerWarnings = document.createElement( 'div' );
|
||||||
markerWarnings.className = 'ext-discussiontools-debughighlighter-comment-warnings';
|
markerWarnings.className = 'ext-discussiontools-debughighlighter-comment-warnings';
|
||||||
markerWarnings.innerText = comment.warnings.join( '\n' );
|
markerWarnings.innerText = comment.warnings.join( '\n' );
|
||||||
}
|
}
|
||||||
|
|
||||||
var update = function () {
|
const update = function () {
|
||||||
var rect = fixFakeFirstHeadingRect(
|
const rect = fixFakeFirstHeadingRect(
|
||||||
comment.getRange().getBoundingClientRect(),
|
comment.getRange().getBoundingClientRect(),
|
||||||
comment
|
comment
|
||||||
);
|
);
|
||||||
var scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
|
const scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
|
||||||
var scrollLeft = document.documentElement.scrollLeft || document.body.scrollLeft;
|
const scrollLeft = document.documentElement.scrollLeft || document.body.scrollLeft;
|
||||||
|
|
||||||
marker.style.top = ( rect.top + scrollTop ) + 'px';
|
marker.style.top = ( rect.top + scrollTop ) + 'px';
|
||||||
marker.style.height = ( rect.height ) + 'px';
|
marker.style.height = ( rect.height ) + 'px';
|
||||||
|
@ -91,7 +91,7 @@ function markComment( comment ) {
|
||||||
marker.style.width = ( rect.width ) + 'px';
|
marker.style.width = ( rect.width ) + 'px';
|
||||||
|
|
||||||
if ( marker2 ) {
|
if ( marker2 ) {
|
||||||
var parentRect = comment.parent.getRange().getBoundingClientRect();
|
let parentRect = comment.parent.getRange().getBoundingClientRect();
|
||||||
parentRect = fixFakeFirstHeadingRect( parentRect, comment.parent );
|
parentRect = fixFakeFirstHeadingRect( parentRect, comment.parent );
|
||||||
if ( comment.parent.level === 0 ) {
|
if ( comment.parent.level === 0 ) {
|
||||||
// Twiddle so that it looks nice
|
// Twiddle so that it looks nice
|
||||||
|
|
|
@ -11,7 +11,7 @@ require( './CommentTarget.js' );
|
||||||
* @param {Object} [config] Configuration options
|
* @param {Object} [config] Configuration options
|
||||||
*/
|
*/
|
||||||
function CommentTargetWidget( replyWidget, config ) {
|
function CommentTargetWidget( replyWidget, config ) {
|
||||||
var excludeCommands = [
|
let excludeCommands = [
|
||||||
'blockquoteWrap', // T258194
|
'blockquoteWrap', // T258194
|
||||||
// Disable to allow Tab/Shift+Tab to move focus out of the widget (T172694)
|
// Disable to allow Tab/Shift+Tab to move focus out of the widget (T172694)
|
||||||
'indent',
|
'indent',
|
||||||
|
@ -75,7 +75,7 @@ CommentTargetWidget.prototype.createTarget = function () {
|
||||||
* @inheritdoc
|
* @inheritdoc
|
||||||
*/
|
*/
|
||||||
CommentTargetWidget.prototype.setDocument = function ( docOrHtml ) {
|
CommentTargetWidget.prototype.setDocument = function ( docOrHtml ) {
|
||||||
var mode = this.target.getDefaultMode(),
|
const mode = this.target.getDefaultMode(),
|
||||||
doc = ( mode === 'visual' && typeof docOrHtml === 'string' ) ?
|
doc = ( mode === 'visual' && typeof docOrHtml === 'string' ) ?
|
||||||
this.target.parseDocument( docOrHtml ) :
|
this.target.parseDocument( docOrHtml ) :
|
||||||
docOrHtml,
|
docOrHtml,
|
||||||
|
|
|
@ -41,7 +41,7 @@ CeMWPingNode.static.getDescription = function ( model ) {
|
||||||
* @inheritdoc
|
* @inheritdoc
|
||||||
*/
|
*/
|
||||||
CeMWPingNode.prototype.initialize = function () {
|
CeMWPingNode.prototype.initialize = function () {
|
||||||
var model = this.getModel(),
|
const model = this.getModel(),
|
||||||
prefix = mw.msg( 'discussiontools-replywidget-mention-prefix' ),
|
prefix = mw.msg( 'discussiontools-replywidget-mention-prefix' ),
|
||||||
suffix = mw.msg( 'discussiontools-replywidget-mention-suffix' ),
|
suffix = mw.msg( 'discussiontools-replywidget-mention-suffix' ),
|
||||||
user = model.getAttribute( 'user' ),
|
user = model.getAttribute( 'user' ),
|
||||||
|
@ -51,7 +51,7 @@ CeMWPingNode.prototype.initialize = function () {
|
||||||
CeMWPingNode.super.prototype.initialize.call( this );
|
CeMWPingNode.super.prototype.initialize.call( this );
|
||||||
|
|
||||||
// DOM changes
|
// DOM changes
|
||||||
var $link = $( '<a>' )
|
const $link = $( '<a>' )
|
||||||
.addClass( 'ext-discussiontools-ce-mwPingNode' )
|
.addClass( 'ext-discussiontools-ce-mwPingNode' )
|
||||||
.attr( {
|
.attr( {
|
||||||
href: title.getUrl(),
|
href: title.getUrl(),
|
||||||
|
|
|
@ -40,7 +40,7 @@ DmMWPingNode.static.matchFunction = function () {
|
||||||
DmMWPingNode.static.disallowedAnnotationTypes = [ 'link' ];
|
DmMWPingNode.static.disallowedAnnotationTypes = [ 'link' ];
|
||||||
|
|
||||||
DmMWPingNode.static.toDomElements = function ( dataElement, doc, converter ) {
|
DmMWPingNode.static.toDomElements = function ( dataElement, doc, converter ) {
|
||||||
var domElements,
|
let domElements,
|
||||||
prefix = mw.msg( 'discussiontools-replywidget-mention-prefix' ),
|
prefix = mw.msg( 'discussiontools-replywidget-mention-prefix' ),
|
||||||
suffix = mw.msg( 'discussiontools-replywidget-mention-suffix' ),
|
suffix = mw.msg( 'discussiontools-replywidget-mention-suffix' ),
|
||||||
title = mw.Title.makeTitle( mw.config.get( 'wgNamespaceIds' ).user, dataElement.attributes.user );
|
title = mw.Title.makeTitle( mw.config.get( 'wgNamespaceIds' ).user, dataElement.attributes.user );
|
||||||
|
|
|
@ -15,9 +15,9 @@ DtUiMWSignatureContextItem.static.label =
|
||||||
// Get the formatted, localized, platform-specific shortcut key for the given command
|
// Get the formatted, localized, platform-specific shortcut key for the given command
|
||||||
DtUiMWSignatureContextItem.prototype.getShortcutKey = function ( commandName ) {
|
DtUiMWSignatureContextItem.prototype.getShortcutKey = function ( commandName ) {
|
||||||
// Adapted from ve.ui.CommandHelpDialog.prototype.initialize
|
// Adapted from ve.ui.CommandHelpDialog.prototype.initialize
|
||||||
var commandInfo = ve.ui.commandHelpRegistry.lookup( commandName );
|
const commandInfo = ve.ui.commandHelpRegistry.lookup( commandName );
|
||||||
var triggerList = ve.ui.triggerRegistry.lookup( commandInfo.trigger );
|
const triggerList = ve.ui.triggerRegistry.lookup( commandInfo.trigger );
|
||||||
var $shortcut = $( '<kbd>' ).addClass( 've-ui-commandHelpDialog-shortcut' ).append(
|
const $shortcut = $( '<kbd>' ).addClass( 've-ui-commandHelpDialog-shortcut' ).append(
|
||||||
triggerList[ 0 ].getMessage( true ).map( ve.ui.CommandHelpDialog.static.buildKeyNode )
|
triggerList[ 0 ].getMessage( true ).map( ve.ui.CommandHelpDialog.static.buildKeyNode )
|
||||||
).find( 'kbd + kbd' ).before( '+' ).end();
|
).find( 'kbd + kbd' ).before( '+' ).end();
|
||||||
return $shortcut;
|
return $shortcut;
|
||||||
|
|
|
@ -37,7 +37,7 @@ function MWUsernameCompletionAction() {
|
||||||
}
|
}
|
||||||
} );
|
} );
|
||||||
// On user talk pages, always list the "owner" of the talk page
|
// 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 (
|
if (
|
||||||
relevantUserName &&
|
relevantUserName &&
|
||||||
relevantUserName !== mw.user.getName() &&
|
relevantUserName !== mw.user.getName() &&
|
||||||
|
@ -67,7 +67,7 @@ MWUsernameCompletionAction.static.methods.push( 'insertAndOpen' );
|
||||||
/* Methods */
|
/* Methods */
|
||||||
|
|
||||||
MWUsernameCompletionAction.prototype.insertAndOpen = function () {
|
MWUsernameCompletionAction.prototype.insertAndOpen = function () {
|
||||||
var inserted = false,
|
let inserted = false,
|
||||||
surfaceModel = this.surface.getModel(),
|
surfaceModel = this.surface.getModel(),
|
||||||
fragment = surfaceModel.getFragment();
|
fragment = surfaceModel.getFragment();
|
||||||
|
|
||||||
|
@ -106,12 +106,12 @@ MWUsernameCompletionAction.prototype.getSequenceLength = function () {
|
||||||
};
|
};
|
||||||
|
|
||||||
MWUsernameCompletionAction.prototype.getSuggestions = function ( input ) {
|
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 : '',
|
validatedInput = title ? input : '',
|
||||||
action = this;
|
action = this;
|
||||||
|
|
||||||
this.api.abort(); // Abort all unfinished API requests
|
this.api.abort(); // Abort all unfinished API requests
|
||||||
var apiPromise;
|
let apiPromise;
|
||||||
if ( input.length > 0 && !this.searchedPrefixes[ input ] ) {
|
if ( input.length > 0 && !this.searchedPrefixes[ input ] ) {
|
||||||
apiPromise = this.api.get( {
|
apiPromise = this.api.get( {
|
||||||
action: 'query',
|
action: 'query',
|
||||||
|
@ -123,7 +123,7 @@ MWUsernameCompletionAction.prototype.getSuggestions = function ( input ) {
|
||||||
// blocked users and still probably have some suggestions left
|
// blocked users and still probably have some suggestions left
|
||||||
aulimit: this.constructor.static.defaultLimit * 2
|
aulimit: this.constructor.static.defaultLimit * 2
|
||||||
} ).then( ( response ) => {
|
} ).then( ( response ) => {
|
||||||
var suggestions = response.query.allusers.filter(
|
const suggestions = response.query.allusers.filter(
|
||||||
// API doesn't return IPs
|
// API doesn't return IPs
|
||||||
( user ) => !hasUser( action.localUsers, user.name ) &&
|
( user ) => !hasUser( action.localUsers, user.name ) &&
|
||||||
!hasUser( action.remoteUsers, user.name ) &&
|
!hasUser( action.remoteUsers, user.name ) &&
|
||||||
|
@ -167,7 +167,7 @@ MWUsernameCompletionAction.prototype.getSuggestions = function ( input ) {
|
||||||
* @inheritdoc
|
* @inheritdoc
|
||||||
*/
|
*/
|
||||||
MWUsernameCompletionAction.prototype.compareSuggestionToInput = function ( suggestion, normalizedInput ) {
|
MWUsernameCompletionAction.prototype.compareSuggestionToInput = function ( suggestion, normalizedInput ) {
|
||||||
var normalizedSuggestion = suggestion.username.toLowerCase(),
|
const normalizedSuggestion = suggestion.username.toLowerCase(),
|
||||||
normalizedSearchIndex = normalizedSuggestion + ' ' +
|
normalizedSearchIndex = normalizedSuggestion + ' ' +
|
||||||
suggestion.displayNames
|
suggestion.displayNames
|
||||||
.map( ( displayName ) => displayName.toLowerCase() ).join( ' ' );
|
.map( ( displayName ) => displayName.toLowerCase() ).join( ' ' );
|
||||||
|
@ -197,13 +197,13 @@ MWUsernameCompletionAction.prototype.getMenuItemForSuggestion = function ( sugge
|
||||||
|
|
||||||
MWUsernameCompletionAction.prototype.getHeaderLabel = function ( input, suggestions ) {
|
MWUsernameCompletionAction.prototype.getHeaderLabel = function ( input, suggestions ) {
|
||||||
if ( suggestions === undefined ) {
|
if ( suggestions === undefined ) {
|
||||||
var $query = $( '<span>' ).text( input );
|
const $query = $( '<span>' ).text( input );
|
||||||
return mw.message( 'discussiontools-replywidget-mention-tool-header', $query ).parseDom();
|
return mw.message( 'discussiontools-replywidget-mention-tool-header', $query ).parseDom();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
MWUsernameCompletionAction.prototype.insertCompletion = function ( word, range ) {
|
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' ),
|
suffix = mw.msg( 'discussiontools-replywidget-mention-suffix' ),
|
||||||
title = mw.Title.newFromText( word, mw.config.get( 'wgNamespaceIds' ).user );
|
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 );
|
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( [
|
fragment.removeContent().insertContent( [
|
||||||
{ type: 'mwPing', attributes: { user: word } },
|
{ type: 'mwPing', attributes: { user: word } },
|
||||||
{ type: '/mwPing' }
|
{ type: '/mwPing' }
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// Adapted from ve.ui.MWWikitextDataTransferHandlerFactory
|
// Adapted from ve.ui.MWWikitextDataTransferHandlerFactory
|
||||||
function importRegistry( parent, child ) {
|
function importRegistry( parent, child ) {
|
||||||
var name;
|
let name;
|
||||||
// Copy existing items
|
// Copy existing items
|
||||||
for ( name in parent.registry ) {
|
for ( name in parent.registry ) {
|
||||||
child.register( parent.registry[ name ] );
|
child.register( parent.registry[ name ] );
|
||||||
|
|
|
@ -22,13 +22,13 @@ if ( debug & DEBUG_HIGHLIGHT ) {
|
||||||
|
|
||||||
comments.forEach( ( comment ) => {
|
comments.forEach( ( comment ) => {
|
||||||
comment.signatureRanges.forEach( ( signatureRange ) => {
|
comment.signatureRanges.forEach( ( signatureRange ) => {
|
||||||
var node = signatureRange.endContainer;
|
const node = signatureRange.endContainer;
|
||||||
var match = parser.findTimestamp( node, timestampRegexps );
|
const match = parser.findTimestamp( node, timestampRegexps );
|
||||||
if ( !match ) {
|
if ( !match ) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var signature = parser.findSignature( node ).nodes;
|
const signature = parser.findSignature( node ).nodes;
|
||||||
var emptySignature = signature.length === 1 && signature[ 0 ] === node;
|
const emptySignature = signature.length === 1 && signature[ 0 ] === node;
|
||||||
// Note that additional content may follow the timestamp (e.g. in some voting formats), but we
|
// 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,
|
// 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.
|
// 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
|
// eslint-disable-next-line no-bitwise
|
||||||
if ( ( debug & DEBUG_VOTE ) || ( debug & DEBUG_VOTE_PERMISSIVE ) ) {
|
if ( ( debug & DEBUG_VOTE ) || ( debug & DEBUG_VOTE_PERMISSIVE ) ) {
|
||||||
threads.forEach( ( thread ) => {
|
threads.forEach( ( thread ) => {
|
||||||
var firstComment = thread.replies[ 0 ];
|
const firstComment = thread.replies[ 0 ];
|
||||||
|
|
||||||
if ( firstComment && firstComment.type === 'comment' ) {
|
if ( firstComment && firstComment.type === 'comment' ) {
|
||||||
// eslint-disable-next-line no-bitwise
|
// eslint-disable-next-line no-bitwise
|
||||||
|
@ -52,7 +52,7 @@ if ( ( debug & DEBUG_VOTE ) || ( debug & DEBUG_VOTE_PERMISSIVE ) ) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var firstVote = firstComment.level === 1 ?
|
const firstVote = firstComment.level === 1 ?
|
||||||
// In permissive mode, the first vote is the replies to the OP
|
// In permissive mode, the first vote is the replies to the OP
|
||||||
firstComment.replies[ 0 ] :
|
firstComment.replies[ 0 ] :
|
||||||
firstComment;
|
firstComment;
|
||||||
|
@ -61,15 +61,15 @@ if ( ( debug & DEBUG_VOTE ) || ( debug & DEBUG_VOTE_PERMISSIVE ) ) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var lastReply;
|
let lastReply;
|
||||||
var level = firstVote.level;
|
const level = firstVote.level;
|
||||||
firstVote.parent.replies.forEach( ( reply ) => {
|
firstVote.parent.replies.forEach( ( reply ) => {
|
||||||
if ( reply.type === 'comment' && reply.level <= level ) {
|
if ( reply.type === 'comment' && reply.level <= level ) {
|
||||||
lastReply = reply;
|
lastReply = reply;
|
||||||
}
|
}
|
||||||
} );
|
} );
|
||||||
|
|
||||||
var listItem = modifier.addSiblingListItem(
|
const listItem = modifier.addSiblingListItem(
|
||||||
utils.closestElement( lastReply.range.endContainer, [ 'li', 'dd', 'p' ] )
|
utils.closestElement( lastReply.range.endContainer, [ 'li', 'dd', 'p' ] )
|
||||||
);
|
);
|
||||||
if ( listItem && listItem.tagName.toLowerCase() === 'li' ) {
|
if ( listItem && listItem.tagName.toLowerCase() === 'li' ) {
|
||||||
|
|
|
@ -63,7 +63,7 @@ mw.dt.init = function ( $container ) {
|
||||||
// If it's a full page live preview, (re)initialize to support highlighting comments (T309423)
|
// 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 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
|
// 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 ) ) {
|
if ( $container.parent().is( livePreviewSelectors ) ) {
|
||||||
reallyInit( $container );
|
reallyInit( $container );
|
||||||
return;
|
return;
|
||||||
|
@ -94,7 +94,7 @@ if ( mw.config.get( 'wgAction' ) === 'history' ) {
|
||||||
// TODO: Remove this code after a few weeks.
|
// TODO: Remove this code after a few weeks.
|
||||||
mw.requestIdleCallback( () => {
|
mw.requestIdleCallback( () => {
|
||||||
try {
|
try {
|
||||||
for ( var key in localStorage ) {
|
for ( const key in localStorage ) {
|
||||||
if ( key.startsWith( 'reply/' ) ) {
|
if ( key.startsWith( 'reply/' ) ) {
|
||||||
localStorage.removeItem( key );
|
localStorage.removeItem( key );
|
||||||
localStorage.removeItem( '_EXPIRY_' + key );
|
localStorage.removeItem( '_EXPIRY_' + key );
|
||||||
|
|
|
@ -31,7 +31,7 @@ function ReplyWidget( commentController, commentDetails, config ) {
|
||||||
this.pending = false;
|
this.pending = false;
|
||||||
this.isTornDown = false;
|
this.isTornDown = false;
|
||||||
this.commentController = commentController;
|
this.commentController = commentController;
|
||||||
var threadItem = commentController.getThreadItem();
|
const threadItem = commentController.getThreadItem();
|
||||||
this.commentDetails = commentDetails;
|
this.commentDetails = commentDetails;
|
||||||
this.isNewTopic = !!threadItem.isNewTopic;
|
this.isNewTopic = !!threadItem.isNewTopic;
|
||||||
this.pageName = commentDetails.pageName;
|
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
|
// pageExists can only be false for the new comment tool, so we
|
||||||
// don't need to worry about transcluded replies.
|
// don't need to worry about transcluded replies.
|
||||||
this.pageExists = mw.config.get( 'wgRelevantArticleId', 0 ) !== 0;
|
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.context = contextNode ? contextNode.tagName.toLowerCase() : 'dl';
|
||||||
this.storage = commentController.storage;
|
this.storage = commentController.storage;
|
||||||
// eslint-disable-next-line no-jquery/no-global-selector
|
// eslint-disable-next-line no-jquery/no-global-selector
|
||||||
|
@ -51,7 +51,7 @@ function ReplyWidget( commentController, commentDetails, config ) {
|
||||||
this.$window = $( this.getElementWindow() );
|
this.$window = $( this.getElementWindow() );
|
||||||
this.onWindowScrollThrottled = OO.ui.throttle( this.onWindowScroll.bind( this ), 100 );
|
this.onWindowScrollThrottled = OO.ui.throttle( this.onWindowScroll.bind( this ), 100 );
|
||||||
|
|
||||||
var inputConfig = Object.assign(
|
const inputConfig = Object.assign(
|
||||||
{
|
{
|
||||||
placeholder: this.isNewTopic ?
|
placeholder: this.isNewTopic ?
|
||||||
mw.msg( 'discussiontools-replywidget-placeholder-newtopic' ) :
|
mw.msg( 'discussiontools-replywidget-placeholder-newtopic' ) :
|
||||||
|
@ -172,7 +172,7 @@ function ReplyWidget( commentController, commentDetails, config ) {
|
||||||
mw.message( 'discussiontools-replywidget-transcluded', this.pageName ).parseDom()
|
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() ) {
|
if ( mw.user.isNamed() ) {
|
||||||
$footerLinks.append(
|
$footerLinks.append(
|
||||||
$( '<li>' ).append(
|
$( '<li>' ).append(
|
||||||
|
@ -268,10 +268,10 @@ function ReplyWidget( commentController, commentDetails, config ) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( mw.user.isAnon() ) {
|
if ( mw.user.isAnon() ) {
|
||||||
var msg = this.commentDetails.wouldAutoCreate ?
|
const msg = this.commentDetails.wouldAutoCreate ?
|
||||||
'discussiontools-replywidget-autocreate-warning' :
|
'discussiontools-replywidget-autocreate-warning' :
|
||||||
'discussiontools-replywidget-anon-warning';
|
'discussiontools-replywidget-anon-warning';
|
||||||
var returnTo = {
|
const returnTo = {
|
||||||
returntoquery: window.location.search.slice( 1 ),
|
returntoquery: window.location.search.slice( 1 ),
|
||||||
returnto: mw.config.get( 'wgPageName' )
|
returnto: mw.config.get( 'wgPageName' )
|
||||||
};
|
};
|
||||||
|
@ -310,7 +310,7 @@ function ReplyWidget( commentController, commentDetails, config ) {
|
||||||
this.editSummaryField.$body.append( this.$checkboxes );
|
this.editSummaryField.$body.append( this.$checkboxes );
|
||||||
|
|
||||||
// bind logging:
|
// 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 ) );
|
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
|
* Handle window scroll events
|
||||||
*/
|
*/
|
||||||
ReplyWidget.prototype.onWindowScroll = function () {
|
ReplyWidget.prototype.onWindowScroll = function () {
|
||||||
var rect = this.$element[ 0 ].getBoundingClientRect();
|
const rect = this.$element[ 0 ].getBoundingClientRect();
|
||||||
var viewportHeight = window.visualViewport ? visualViewport.height : this.viewportScrollContainer.clientHeight;
|
const viewportHeight = window.visualViewport ? visualViewport.height : this.viewportScrollContainer.clientHeight;
|
||||||
var floating = rect.bottom < 0 ? 'top' :
|
const floating = rect.bottom < 0 ? 'top' :
|
||||||
( rect.top > viewportHeight ? 'bottom' : null );
|
( rect.top > viewportHeight ? 'bottom' : null );
|
||||||
|
|
||||||
if ( floating !== this.floating ) {
|
if ( floating !== this.floating ) {
|
||||||
|
@ -479,7 +479,7 @@ ReplyWidget.prototype.saveEditMode = function ( mode ) {
|
||||||
};
|
};
|
||||||
|
|
||||||
ReplyWidget.prototype.onAdvancedToggleClick = function () {
|
ReplyWidget.prototype.onAdvancedToggleClick = function () {
|
||||||
var showAdvanced = !this.showAdvanced;
|
const showAdvanced = !this.showAdvanced;
|
||||||
mw.track( 'visualEditorFeatureUse', {
|
mw.track( 'visualEditorFeatureUse', {
|
||||||
feature: 'dtReply',
|
feature: 'dtReply',
|
||||||
action: 'advanced-' + ( showAdvanced ? 'show' : 'hide' )
|
action: 'advanced-' + ( showAdvanced ? 'show' : 'hide' )
|
||||||
|
@ -490,20 +490,20 @@ ReplyWidget.prototype.onAdvancedToggleClick = function () {
|
||||||
this.toggleAdvanced( showAdvanced );
|
this.toggleAdvanced( showAdvanced );
|
||||||
|
|
||||||
if ( 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
|
// 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.
|
// make it easier to change. Otherwise, move cursor to end.
|
||||||
var selectFromIndex = summary.length;
|
let selectFromIndex = summary.length;
|
||||||
if ( this.isNewTopic ) {
|
if ( this.isNewTopic ) {
|
||||||
var titleText = this.commentController.sectionTitle.getValue();
|
const titleText = this.commentController.sectionTitle.getValue();
|
||||||
if ( summary === this.commentController.generateSummary( titleText ) ) {
|
if ( summary === this.commentController.generateSummary( titleText ) ) {
|
||||||
selectFromIndex = titleText.length + '/* '.length + ' */ '.length;
|
selectFromIndex = titleText.length + '/* '.length + ' */ '.length;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Same as summary.endsWith( defaultReplyTrail )
|
// Same as summary.endsWith( defaultReplyTrail )
|
||||||
var defaultReplyTrail = '*/ ' + mw.msg( 'discussiontools-defaultsummary-reply' );
|
const defaultReplyTrail = '*/ ' + mw.msg( 'discussiontools-defaultsummary-reply' );
|
||||||
var endCommentIndex = summary.indexOf( defaultReplyTrail );
|
const endCommentIndex = summary.indexOf( defaultReplyTrail );
|
||||||
if ( endCommentIndex + defaultReplyTrail.length === summary.length ) {
|
if ( endCommentIndex + defaultReplyTrail.length === summary.length ) {
|
||||||
selectFromIndex = endCommentIndex + 3;
|
selectFromIndex = endCommentIndex + 3;
|
||||||
}
|
}
|
||||||
|
@ -537,7 +537,7 @@ ReplyWidget.prototype.getEditSummary = function () {
|
||||||
};
|
};
|
||||||
|
|
||||||
ReplyWidget.prototype.onModeTabSelectChoose = function ( option ) {
|
ReplyWidget.prototype.onModeTabSelectChoose = function ( option ) {
|
||||||
var mode = option.getData();
|
const mode = option.getData();
|
||||||
|
|
||||||
if ( mode === this.getMode() ) {
|
if ( mode === this.getMode() ) {
|
||||||
return;
|
return;
|
||||||
|
@ -560,7 +560,7 @@ ReplyWidget.prototype.switch = function ( mode ) {
|
||||||
return $.Deferred().reject().promise();
|
return $.Deferred().reject().promise();
|
||||||
}
|
}
|
||||||
|
|
||||||
var promise;
|
let promise;
|
||||||
this.setPending( true );
|
this.setPending( true );
|
||||||
switch ( mode ) {
|
switch ( mode ) {
|
||||||
case 'source':
|
case 'source':
|
||||||
|
@ -609,7 +609,7 @@ ReplyWidget.prototype.setup = function ( data ) {
|
||||||
}
|
}
|
||||||
this.saveEditMode( this.getMode() );
|
this.saveEditMode( this.getMode() );
|
||||||
|
|
||||||
var summary = this.storage.get( 'summary' ) || data.editSummary;
|
let summary = this.storage.get( 'summary' ) || data.editSummary;
|
||||||
|
|
||||||
if ( !summary ) {
|
if ( !summary ) {
|
||||||
if ( this.isNewTopic ) {
|
if ( this.isNewTopic ) {
|
||||||
|
@ -617,7 +617,7 @@ ReplyWidget.prototype.setup = function ( data ) {
|
||||||
// in NewTopicController#onSectionTitleChange
|
// in NewTopicController#onSectionTitleChange
|
||||||
summary = '';
|
summary = '';
|
||||||
} else {
|
} else {
|
||||||
var title = this.commentController.getThreadItem().getHeading().getLinkableTitle();
|
const title = this.commentController.getThreadItem().getHeading().getLinkableTitle();
|
||||||
summary = ( title ? '/* ' + title + ' */ ' : '' ) +
|
summary = ( title ? '/* ' + title + ' */ ' : '' ) +
|
||||||
mw.msg( 'discussiontools-defaultsummary-reply' );
|
mw.msg( 'discussiontools-defaultsummary-reply' );
|
||||||
}
|
}
|
||||||
|
@ -663,10 +663,10 @@ ReplyWidget.prototype.afterSetup = function () {
|
||||||
* @return {string} Form token
|
* @return {string} Form token
|
||||||
*/
|
*/
|
||||||
ReplyWidget.prototype.getFormToken = function () {
|
ReplyWidget.prototype.getFormToken = function () {
|
||||||
var formToken = this.storage.get( 'formToken' );
|
let formToken = this.storage.get( 'formToken' );
|
||||||
if ( !formToken ) {
|
if ( !formToken ) {
|
||||||
// See ApiBase::PARAM_MAX_CHARS in ApiDiscussionToolsEdit.php
|
// See ApiBase::PARAM_MAX_CHARS in ApiDiscussionToolsEdit.php
|
||||||
var maxLength = 16;
|
const maxLength = 16;
|
||||||
formToken = Math.random().toString( 36 ).slice( 2, maxLength + 2 );
|
formToken = Math.random().toString( 36 ).slice( 2, maxLength + 2 );
|
||||||
this.storage.set( 'formToken', formToken );
|
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
|
* @return {jQuery.Promise} Resolves if widget was torn down, rejects if it wasn't
|
||||||
*/
|
*/
|
||||||
ReplyWidget.prototype.tryTeardown = function () {
|
ReplyWidget.prototype.tryTeardown = function () {
|
||||||
var promise;
|
let promise;
|
||||||
|
|
||||||
if ( !this.isEmpty() || ( this.isNewTopic && this.commentController.sectionTitle.getValue() ) ) {
|
if ( !this.isEmpty() || ( this.isNewTopic && this.commentController.sectionTitle.getValue() ) ) {
|
||||||
promise = OO.ui.getWindowManager().openWindow( this.isNewTopic ? 'abandontopic' : 'abandoncomment' )
|
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 = wikitext !== undefined ? wikitext : this.getValue();
|
||||||
wikitext = utils.htmlTrim( wikitext );
|
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 ) {
|
if ( this.previewWikitext === wikitext && this.previewTitle === title ) {
|
||||||
return $.Deferred().resolve().promise();
|
return $.Deferred().resolve().promise();
|
||||||
|
@ -828,7 +828,7 @@ ReplyWidget.prototype.preparePreview = function ( wikitext ) {
|
||||||
this.previewRequest = null;
|
this.previewRequest = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var parsePromise;
|
let parsePromise;
|
||||||
if ( !wikitext ) {
|
if ( !wikitext ) {
|
||||||
parsePromise = $.Deferred().resolve( null ).promise();
|
parsePromise = $.Deferred().resolve( null ).promise();
|
||||||
} else {
|
} else {
|
||||||
|
@ -1029,7 +1029,7 @@ ReplyWidget.prototype.onNewCommentsCloseClick = function () {
|
||||||
* @return {OO.ui.MessageWidget} Message widget
|
* @return {OO.ui.MessageWidget} Message widget
|
||||||
*/
|
*/
|
||||||
ReplyWidget.prototype.createErrorMessage = function ( message ) {
|
ReplyWidget.prototype.createErrorMessage = function ( message ) {
|
||||||
var errorMessage = new OO.ui.MessageWidget( {
|
const errorMessage = new OO.ui.MessageWidget( {
|
||||||
type: 'error',
|
type: 'error',
|
||||||
label: message,
|
label: message,
|
||||||
classes: [ 'ext-discussiontools-ui-replyWidget-error' ]
|
classes: [ 'ext-discussiontools-ui-replyWidget-error' ]
|
||||||
|
@ -1058,12 +1058,12 @@ ReplyWidget.prototype.onReplyClick = function () {
|
||||||
mw.track( 'editAttemptStep', { action: 'saveIntent' } );
|
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
|
// 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' } );
|
mw.track( 'editAttemptStep', { action: 'saveAttempt' } );
|
||||||
this.commentController.save( pageName ).fail( ( code, data ) => {
|
this.commentController.save( pageName ).fail( ( code, data ) => {
|
||||||
// Compare to ve.init.mw.ArticleTargetEvents.js in VisualEditor.
|
// Compare to ve.init.mw.ArticleTargetEvents.js in VisualEditor.
|
||||||
var typeMap = {
|
const typeMap = {
|
||||||
badtoken: 'userBadToken',
|
badtoken: 'userBadToken',
|
||||||
assertanonfailed: 'userNewUser',
|
assertanonfailed: 'userNewUser',
|
||||||
assertuserfailed: 'userNewUser',
|
assertuserfailed: 'userNewUser',
|
||||||
|
|
|
@ -15,7 +15,7 @@ function ReplyWidgetPlain() {
|
||||||
ReplyWidgetPlain.super.apply( this, arguments );
|
ReplyWidgetPlain.super.apply( this, arguments );
|
||||||
|
|
||||||
if ( OO.ui.isMobile() ) {
|
if ( OO.ui.isMobile() ) {
|
||||||
var toolFactory = new OO.ui.ToolFactory(),
|
const toolFactory = new OO.ui.ToolFactory(),
|
||||||
toolGroupFactory = new OO.ui.ToolGroupFactory();
|
toolGroupFactory = new OO.ui.ToolGroupFactory();
|
||||||
|
|
||||||
toolFactory.register( mw.libs.ve.MWEditModeVisualTool );
|
toolFactory.register( mw.libs.ve.MWEditModeVisualTool );
|
||||||
|
@ -56,7 +56,7 @@ OO.inheritClass( ReplyWidgetPlain, require( './dt.ui.ReplyWidget.js' ) );
|
||||||
* @inheritdoc
|
* @inheritdoc
|
||||||
*/
|
*/
|
||||||
ReplyWidgetPlain.prototype.createReplyBodyWidget = function ( config ) {
|
ReplyWidgetPlain.prototype.createReplyBodyWidget = function ( config ) {
|
||||||
var textInput = new OO.ui.MultilineTextInputWidget( Object.assign( {
|
const textInput = new OO.ui.MultilineTextInputWidget( Object.assign( {
|
||||||
rows: 3,
|
rows: 3,
|
||||||
// TODO: Fix upstream to support a value meaning no max limit (e.g. Infinity)
|
// TODO: Fix upstream to support a value meaning no max limit (e.g. Infinity)
|
||||||
maxRows: 999,
|
maxRows: 999,
|
||||||
|
@ -123,7 +123,7 @@ ReplyWidgetPlain.prototype.onInputChange = function () {
|
||||||
// Parent method
|
// Parent method
|
||||||
ReplyWidgetPlain.super.prototype.onInputChange.apply( this, arguments );
|
ReplyWidgetPlain.super.prototype.onInputChange.apply( this, arguments );
|
||||||
|
|
||||||
var wikitext = this.getValue();
|
const wikitext = this.getValue();
|
||||||
this.storage.set( 'body', wikitext );
|
this.storage.set( 'body', wikitext );
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -131,7 +131,7 @@ ReplyWidgetPlain.prototype.onInputChange = function () {
|
||||||
* @inheritdoc
|
* @inheritdoc
|
||||||
*/
|
*/
|
||||||
ReplyWidgetPlain.prototype.setup = function ( data ) {
|
ReplyWidgetPlain.prototype.setup = function ( data ) {
|
||||||
var autosaveValue = this.storage.get( 'body' );
|
const autosaveValue = this.storage.get( 'body' );
|
||||||
|
|
||||||
data = data || {};
|
data = data || {};
|
||||||
|
|
||||||
|
|
|
@ -73,7 +73,7 @@ ReplyWidgetVisual.prototype.clear = function ( preserveStorage ) {
|
||||||
* @inheritdoc
|
* @inheritdoc
|
||||||
*/
|
*/
|
||||||
ReplyWidgetVisual.prototype.isEmpty = function () {
|
ReplyWidgetVisual.prototype.isEmpty = function () {
|
||||||
var surface = this.replyBodyWidget.target.getSurface();
|
const surface = this.replyBodyWidget.target.getSurface();
|
||||||
return !( surface && surface.getModel().getDocument().data.hasContent() );
|
return !( surface && surface.getModel().getDocument().data.hasContent() );
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -90,11 +90,11 @@ ReplyWidgetVisual.prototype.getMode = function () {
|
||||||
* @inheritdoc
|
* @inheritdoc
|
||||||
*/
|
*/
|
||||||
ReplyWidgetVisual.prototype.setup = function ( data, suppressNotifications ) {
|
ReplyWidgetVisual.prototype.setup = function ( data, suppressNotifications ) {
|
||||||
var target = this.replyBodyWidget.target;
|
const target = this.replyBodyWidget.target;
|
||||||
|
|
||||||
data = data || {};
|
data = data || {};
|
||||||
|
|
||||||
var htmlOrDoc;
|
let htmlOrDoc;
|
||||||
if ( this.storage.get( 'saveable' ) ) {
|
if ( this.storage.get( 'saveable' ) ) {
|
||||||
htmlOrDoc = this.storage.get( 've-dochtml' );
|
htmlOrDoc = this.storage.get( 've-dochtml' );
|
||||||
target.recovered = true;
|
target.recovered = true;
|
||||||
|
@ -152,7 +152,7 @@ ReplyWidgetVisual.prototype.teardown = function () {
|
||||||
* @inheritdoc
|
* @inheritdoc
|
||||||
*/
|
*/
|
||||||
ReplyWidgetVisual.prototype.focus = function () {
|
ReplyWidgetVisual.prototype.focus = function () {
|
||||||
var targetWidget = this.replyBodyWidget;
|
const targetWidget = this.replyBodyWidget;
|
||||||
setTimeout( () => {
|
setTimeout( () => {
|
||||||
// Check surface still exists after timeout
|
// Check surface still exists after timeout
|
||||||
if ( targetWidget.getSurface() ) {
|
if ( targetWidget.getSurface() ) {
|
||||||
|
|
|
@ -12,8 +12,8 @@ var
|
||||||
* @param {ThreadItem|ThreadItem[]} items Thread item(s) to highlight
|
* @param {ThreadItem|ThreadItem[]} items Thread item(s) to highlight
|
||||||
*/
|
*/
|
||||||
function Highlight( items ) {
|
function Highlight( items ) {
|
||||||
var highlightNodes = [];
|
const highlightNodes = [];
|
||||||
var ranges = [];
|
const ranges = [];
|
||||||
|
|
||||||
this.topmostElement = null;
|
this.topmostElement = null;
|
||||||
|
|
||||||
|
@ -22,12 +22,12 @@ function Highlight( items ) {
|
||||||
this.rootNode = items[ 0 ] ? items[ 0 ].rootNode : null;
|
this.rootNode = items[ 0 ] ? items[ 0 ].rootNode : null;
|
||||||
|
|
||||||
items.forEach( ( item ) => {
|
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
|
// 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.
|
// when it shifts (e.g. collapsing the table of contents), and disappears when it is hidden (e.g.
|
||||||
// opening visual editor).
|
// opening visual editor).
|
||||||
var range = item.getRange();
|
const range = item.getRange();
|
||||||
// Support: Firefox
|
// Support: Firefox
|
||||||
// The highlight node must be inserted after the start marker node (data-mw-comment-start), not
|
// The highlight node must be inserted after the start marker node (data-mw-comment-start), not
|
||||||
// before, otherwise Node#getBoundingClientRect() returns wrong results.
|
// 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 the item is a top-level comment wrapped in a frame, highlight outside that frame
|
||||||
if ( item.level === 1 ) {
|
if ( item.level === 1 ) {
|
||||||
var coveredSiblings = utils.getFullyCoveredSiblings( item, item.rootNode );
|
const coveredSiblings = utils.getFullyCoveredSiblings( item, item.rootNode );
|
||||||
if ( coveredSiblings ) {
|
if ( coveredSiblings ) {
|
||||||
range.setStartBefore( coveredSiblings[ 0 ] );
|
range.setStartBefore( coveredSiblings[ 0 ] );
|
||||||
range.setEndAfter( coveredSiblings[ coveredSiblings.length - 1 ] );
|
range.setEndAfter( coveredSiblings[ coveredSiblings.length - 1 ] );
|
||||||
|
@ -86,13 +86,13 @@ Highlight.prototype.update = function () {
|
||||||
width: '',
|
width: '',
|
||||||
height: ''
|
height: ''
|
||||||
} );
|
} );
|
||||||
var rootRect = this.rootNode.getBoundingClientRect();
|
const rootRect = this.rootNode.getBoundingClientRect();
|
||||||
this.topmostElement = null;
|
this.topmostElement = null;
|
||||||
var topmostTop = Infinity;
|
let topmostTop = Infinity;
|
||||||
this.ranges.forEach( ( range, i ) => {
|
this.ranges.forEach( ( range, i ) => {
|
||||||
var $element = this.$element.eq( i );
|
const $element = this.$element.eq( i );
|
||||||
var baseRect = $element[ 0 ].getBoundingClientRect();
|
const baseRect = $element[ 0 ].getBoundingClientRect();
|
||||||
var rect = RangeFix.getBoundingClientRect( range );
|
const rect = RangeFix.getBoundingClientRect( range );
|
||||||
// rect may be null if the range is in a detached or hidden node
|
// rect may be null if the range is in a detached or hidden node
|
||||||
if ( rect ) {
|
if ( rect ) {
|
||||||
// Draw the highlight over the full width of the page, except for very short comments
|
// 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
|
// 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.
|
// 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 (
|
if (
|
||||||
featuresEnabled.visualenhancements &&
|
featuresEnabled.visualenhancements &&
|
||||||
$element.closest( '.ext-discussiontools-init-section' ).length
|
$element.closest( '.ext-discussiontools-init-section' ).length
|
||||||
|
@ -116,10 +116,10 @@ Highlight.prototype.update = function () {
|
||||||
headingTopAdj = 10;
|
headingTopAdj = 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
var top = rect.top - baseRect.top;
|
const top = rect.top - baseRect.top;
|
||||||
var width = rect.width;
|
let width = rect.width;
|
||||||
var height = rect.height;
|
const height = rect.height;
|
||||||
var left, right;
|
let left, right;
|
||||||
if ( $element.css( 'direction' ) === 'ltr' ) {
|
if ( $element.css( 'direction' ) === 'ltr' ) {
|
||||||
left = rect.left - baseRect.left;
|
left = rect.left - baseRect.left;
|
||||||
if ( useFullWidth ) {
|
if ( useFullWidth ) {
|
||||||
|
@ -131,7 +131,7 @@ Highlight.prototype.update = function () {
|
||||||
width = rootRect.width - ( ( rootRect.left + rootRect.width ) - ( rect.left + rect.width ) );
|
width = rootRect.width - ( ( rootRect.left + rootRect.width ) - ( rect.left + rect.width ) );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var padding = 5;
|
const padding = 5;
|
||||||
$element.css( {
|
$element.css( {
|
||||||
'margin-top': top - padding + headingTopAdj,
|
'margin-top': top - padding + headingTopAdj,
|
||||||
'margin-left': left !== undefined ? left - padding : '',
|
'margin-left': left !== undefined ? left - padding : '',
|
||||||
|
@ -179,11 +179,11 @@ function highlightTargetComment( threadItemSet, noScroll ) {
|
||||||
highlightedTarget = null;
|
highlightedTarget = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var targetElement = mw.util.getTargetFromFragment();
|
const targetElement = mw.util.getTargetFromFragment();
|
||||||
|
|
||||||
if ( targetElement && targetElement.hasAttribute( 'data-mw-comment-start' ) ) {
|
if ( targetElement && targetElement.hasAttribute( 'data-mw-comment-start' ) ) {
|
||||||
var threadItemId = targetElement.getAttribute( 'id' );
|
const threadItemId = targetElement.getAttribute( 'id' );
|
||||||
var threadItem = threadItemSet.findCommentById( targetElement.getAttribute( 'id' ) );
|
const threadItem = threadItemSet.findCommentById( targetElement.getAttribute( 'id' ) );
|
||||||
if ( threadItem ) {
|
if ( threadItem ) {
|
||||||
highlightedTarget = new Highlight( threadItem );
|
highlightedTarget = new Highlight( threadItem );
|
||||||
highlightedTarget.$element.addClass( 'ext-discussiontools-init-targetcomment' );
|
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(
|
return highlightNewComments(
|
||||||
threadItemSet,
|
threadItemSet,
|
||||||
noScroll,
|
noScroll,
|
||||||
|
@ -225,11 +225,11 @@ function highlightTargetComment( threadItemSet, noScroll ) {
|
||||||
* @param {string} threadItemId Thread item ID (NEW_TOPIC_COMMENT_ID for the a new topic)
|
* @param {string} threadItemId Thread item ID (NEW_TOPIC_COMMENT_ID for the a new topic)
|
||||||
*/
|
*/
|
||||||
function highlightPublishedComment( threadItemSet, threadItemId ) {
|
function highlightPublishedComment( threadItemSet, threadItemId ) {
|
||||||
var highlightComments = [];
|
const highlightComments = [];
|
||||||
|
|
||||||
if ( threadItemId === utils.NEW_TOPIC_COMMENT_ID ) {
|
if ( threadItemId === utils.NEW_TOPIC_COMMENT_ID ) {
|
||||||
// Highlight the last comment on the page
|
// 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;
|
lastHighlightedPublishedComment = lastComment;
|
||||||
highlightComments.push( lastComment );
|
highlightComments.push( lastComment );
|
||||||
|
|
||||||
|
@ -244,15 +244,15 @@ function highlightPublishedComment( threadItemSet, threadItemId ) {
|
||||||
lastHighlightedPublishedComment = lastComment.parent;
|
lastHighlightedPublishedComment = lastComment.parent;
|
||||||
// Change URL to point to this section, like the old section=new wikitext editor does.
|
// Change URL to point to this section, like the old section=new wikitext editor does.
|
||||||
// This also expands collapsed sections on mobile (T301840).
|
// This also expands collapsed sections on mobile (T301840).
|
||||||
var sectionTitle = lastHighlightedPublishedComment.getLinkableTitle();
|
const sectionTitle = lastHighlightedPublishedComment.getLinkableTitle();
|
||||||
var urlFragment = mw.util.escapeIdForLink( sectionTitle );
|
const urlFragment = mw.util.escapeIdForLink( sectionTitle );
|
||||||
// Navigate to fragment without scrolling
|
// Navigate to fragment without scrolling
|
||||||
location.hash = '#' + urlFragment + '-DoesNotExist-DiscussionToolsHack';
|
location.hash = '#' + urlFragment + '-DoesNotExist-DiscussionToolsHack';
|
||||||
history.replaceState( null, '', '#' + urlFragment );
|
history.replaceState( null, '', '#' + urlFragment );
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Find the comment we replied to, then highlight the last reply
|
// 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 ] );
|
highlightComments.push( repliedToComment.replies[ repliedToComment.replies.length - 1 ] );
|
||||||
lastHighlightedPublishedComment = highlightComments[ 0 ];
|
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
|
// We may have changed the location hash on mobile, so wait for that to cause
|
||||||
// the section to expand before drawing the highlight.
|
// the section to expand before drawing the highlight.
|
||||||
setTimeout( () => {
|
setTimeout( () => {
|
||||||
var highlight = new Highlight( highlightComments );
|
const highlight = new Highlight( highlightComments );
|
||||||
highlight.$element.addClass( 'ext-discussiontools-init-publishedcomment' );
|
highlight.$element.addClass( 'ext-discussiontools-init-publishedcomment' );
|
||||||
|
|
||||||
// Show a highlight with the same timing as the post-edit message (mediawiki.action.view.postEdit):
|
// 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 || {};
|
options = options || {};
|
||||||
|
|
||||||
if ( options.newCommentsSinceId ) {
|
if ( options.newCommentsSinceId ) {
|
||||||
var newCommentsSince = threadItemSet.findCommentById( options.newCommentsSinceId );
|
const newCommentsSince = threadItemSet.findCommentById( options.newCommentsSinceId );
|
||||||
if ( newCommentsSince && newCommentsSince instanceof CommentItem ) {
|
if ( newCommentsSince && newCommentsSince instanceof CommentItem ) {
|
||||||
var sinceTimestamp = newCommentsSince.timestamp;
|
const sinceTimestamp = newCommentsSince.timestamp;
|
||||||
var threadItems;
|
let threadItems;
|
||||||
if ( options.inThread ) {
|
if ( options.inThread ) {
|
||||||
var heading = newCommentsSince.getSubscribableHeading() || newCommentsSince.getHeading();
|
const heading = newCommentsSince.getSubscribableHeading() || newCommentsSince.getHeading();
|
||||||
threadItems = heading.getThreadItemsBelow();
|
threadItems = heading.getThreadItemsBelow();
|
||||||
} else {
|
} else {
|
||||||
threadItems = threadItemSet.getCommentItems();
|
threadItems = threadItemSet.getCommentItems();
|
||||||
|
@ -332,8 +332,8 @@ function highlightNewComments( threadItemSet, noScroll, newCommentIds, options )
|
||||||
if ( options.sinceThread ) {
|
if ( options.sinceThread ) {
|
||||||
// Check that we are in a thread that is newer than `sinceTimestamp`.
|
// Check that we are in a thread that is newer than `sinceTimestamp`.
|
||||||
// Thread age is determined by looking at getOldestReply.
|
// Thread age is determined by looking at getOldestReply.
|
||||||
var itemHeading = threadItem.getSubscribableHeading() || threadItem.getHeading();
|
const itemHeading = threadItem.getSubscribableHeading() || threadItem.getHeading();
|
||||||
var oldestReply = itemHeading.getOldestReply();
|
const oldestReply = itemHeading.getOldestReply();
|
||||||
if ( !( oldestReply && oldestReply.timestamp >= sinceTimestamp ) ) {
|
if ( !( oldestReply && oldestReply.timestamp >= sinceTimestamp ) ) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -344,7 +344,7 @@ function highlightNewComments( threadItemSet, noScroll, newCommentIds, options )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var comments;
|
let comments;
|
||||||
if ( newCommentIds.length ) {
|
if ( newCommentIds.length ) {
|
||||||
comments = newCommentIds
|
comments = newCommentIds
|
||||||
.map( ( id ) => threadItemSet.findCommentById( id ) )
|
.map( ( id ) => threadItemSet.findCommentById( id ) )
|
||||||
|
@ -372,9 +372,9 @@ function highlightNewComments( threadItemSet, noScroll, newCommentIds, options )
|
||||||
* @param {ThreadItemSet} threadItemSet
|
* @param {ThreadItemSet} threadItemSet
|
||||||
*/
|
*/
|
||||||
function clearHighlightTargetComment( 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' ) ) {
|
if ( targetElement && targetElement.hasAttribute( 'data-mw-comment-start' ) ) {
|
||||||
// Clear the hash from the URL, triggering the 'hashchange' event and updating the :target
|
// Clear the hash from the URL, triggering the 'hashchange' event and updating the :target
|
||||||
|
|
|
@ -8,7 +8,7 @@ var isIos = /ipad|iphone|ipod/i.test( navigator.userAgent );
|
||||||
$( document.body ).toggleClass( 'ext-discussiontools-init-ios', isIos );
|
$( document.body ).toggleClass( 'ext-discussiontools-init-ios', isIos );
|
||||||
|
|
||||||
function onViewportChange() {
|
function onViewportChange() {
|
||||||
var isKeyboardOpen;
|
let isKeyboardOpen;
|
||||||
|
|
||||||
if ( isIos ) {
|
if ( isIos ) {
|
||||||
isKeyboardOpen = visualViewport.height < viewportScrollContainer.clientHeight;
|
isKeyboardOpen = visualViewport.height < viewportScrollContainer.clientHeight;
|
||||||
|
@ -32,20 +32,20 @@ function init( $container ) {
|
||||||
if ( !viewportScrollContainer && window.visualViewport ) {
|
if ( !viewportScrollContainer && window.visualViewport ) {
|
||||||
viewportScrollContainer = OO.ui.Element.static.getClosestScrollableContainer( document.body );
|
viewportScrollContainer = OO.ui.Element.static.getClosestScrollableContainer( document.body );
|
||||||
initialClientHeight = viewportScrollContainer.clientHeight;
|
initialClientHeight = viewportScrollContainer.clientHeight;
|
||||||
var onViewportChangeThrottled = OO.ui.throttle( onViewportChange, 100 );
|
const onViewportChangeThrottled = OO.ui.throttle( onViewportChange, 100 );
|
||||||
$( visualViewport ).on( 'resize', onViewportChangeThrottled );
|
$( visualViewport ).on( 'resize', onViewportChangeThrottled );
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mobile overflow menu
|
// 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
|
// On non-existent pages MobileFrontend wrapping isn't there
|
||||||
.add( $container.find( '.mw-talkpageheader' ) );
|
.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 ) {
|
if ( $ledeButton.length ) {
|
||||||
var windowManager = OO.ui.getWindowManager();
|
const windowManager = OO.ui.getWindowManager();
|
||||||
if ( !ledeSectionDialog ) {
|
if ( !ledeSectionDialog ) {
|
||||||
var LedeSectionDialog = require( './LedeSectionDialog.js' );
|
const LedeSectionDialog = require( './LedeSectionDialog.js' );
|
||||||
ledeSectionDialog = new LedeSectionDialog();
|
ledeSectionDialog = new LedeSectionDialog();
|
||||||
windowManager.addWindows( [ ledeSectionDialog ] );
|
windowManager.addWindows( [ ledeSectionDialog ] );
|
||||||
}
|
}
|
||||||
|
@ -61,7 +61,7 @@ function init( $container ) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line no-jquery/no-global-selector
|
// eslint-disable-next-line no-jquery/no-global-selector
|
||||||
var $newTopicWrapper = $( '.ext-discussiontools-init-new-topic' );
|
const $newTopicWrapper = $( '.ext-discussiontools-init-new-topic' );
|
||||||
|
|
||||||
if (
|
if (
|
||||||
!newTopicButton &&
|
!newTopicButton &&
|
||||||
|
@ -72,16 +72,16 @@ function init( $container ) {
|
||||||
newTopicButton = OO.ui.infuse( $( '.ext-discussiontools-init-new-topic-button' ) );
|
newTopicButton = OO.ui.infuse( $( '.ext-discussiontools-init-new-topic-button' ) );
|
||||||
// For compatibility with MobileWebUIActionsTracking logging (T295490)
|
// For compatibility with MobileWebUIActionsTracking logging (T295490)
|
||||||
newTopicButton.$element.attr( 'data-event-name', 'talkpage.add-topic' );
|
newTopicButton.$element.attr( 'data-event-name', 'talkpage.add-topic' );
|
||||||
var $scrollContainer = $( OO.ui.Element.static.getClosestScrollableContainer( document.body ) );
|
const $scrollContainer = $( OO.ui.Element.static.getClosestScrollableContainer( document.body ) );
|
||||||
var $scrollListener = $scrollContainer.is( 'html, body' ) ? $( OO.ui.Element.static.getWindow( $scrollContainer[ 0 ] ) ) : $scrollContainer;
|
const $scrollListener = $scrollContainer.is( 'html, body' ) ? $( OO.ui.Element.static.getWindow( $scrollContainer[ 0 ] ) ) : $scrollContainer;
|
||||||
var lastScrollTop = $scrollContainer.scrollTop();
|
let lastScrollTop = $scrollContainer.scrollTop();
|
||||||
var wasScrollDown = null;
|
let wasScrollDown = null;
|
||||||
var $body = $( document.body );
|
const $body = $( document.body );
|
||||||
// This block of code is only run once, so we don't need to remove this listener ever
|
// 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( () => {
|
$scrollListener[ 0 ].addEventListener( 'scroll', OO.ui.throttle( () => {
|
||||||
// Round negative values up to 0 to ignore iOS scroll bouncing (T323400)
|
// Round negative values up to 0 to ignore iOS scroll bouncing (T323400)
|
||||||
var scrollTop = Math.max( $scrollContainer.scrollTop(), 0 );
|
const scrollTop = Math.max( $scrollContainer.scrollTop(), 0 );
|
||||||
var isScrollDown = scrollTop > lastScrollTop;
|
const isScrollDown = scrollTop > lastScrollTop;
|
||||||
if ( isScrollDown !== wasScrollDown ) {
|
if ( isScrollDown !== wasScrollDown ) {
|
||||||
if ( !isScrollDown ) {
|
if ( !isScrollDown ) {
|
||||||
$newTopicWrapper.css( 'transition', 'none' );
|
$newTopicWrapper.css( 'transition', 'none' );
|
||||||
|
@ -97,7 +97,7 @@ function init( $container ) {
|
||||||
} );
|
} );
|
||||||
}
|
}
|
||||||
|
|
||||||
var observer = new IntersectionObserver(
|
const observer = new IntersectionObserver(
|
||||||
( ( entries ) => {
|
( ( entries ) => {
|
||||||
$newTopicWrapper.toggleClass( 'ext-discussiontools-init-new-topic-pinned', entries[ 0 ].intersectionRatio === 1 );
|
$newTopicWrapper.toggleClass( 'ext-discussiontools-init-new-topic-pinned', entries[ 0 ].intersectionRatio === 1 );
|
||||||
} ),
|
} ),
|
||||||
|
|
|
@ -24,7 +24,7 @@ function sanitizeWikitextLinebreaks( wikitext ) {
|
||||||
* @param {HTMLElement} linkNode Reply link
|
* @param {HTMLElement} linkNode Reply link
|
||||||
*/
|
*/
|
||||||
function addReplyLink( comment, linkNode ) {
|
function addReplyLink( comment, linkNode ) {
|
||||||
var target = comment.range.endContainer;
|
const target = comment.range.endContainer;
|
||||||
|
|
||||||
// Insert the link before trailing whitespace.
|
// Insert the link before trailing whitespace.
|
||||||
// In the MediaWiki parser output, <ul>/<dl> nodes are preceded by a newline. Normally it isn't
|
// 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}
|
* @return {HTMLElement}
|
||||||
*/
|
*/
|
||||||
function addListItem( comment, replyIndentation ) {
|
function addListItem( comment, replyIndentation ) {
|
||||||
var listTypeMap = {
|
const listTypeMap = {
|
||||||
li: 'ul',
|
li: 'ul',
|
||||||
dd: 'dl'
|
dd: 'dl'
|
||||||
};
|
};
|
||||||
|
@ -62,13 +62,13 @@ function addListItem( comment, replyIndentation ) {
|
||||||
// (or in other words, all replies, and replies to replies, and so on)
|
// (or in other words, all replies, and replies to replies, and so on)
|
||||||
// 3. Add comment with level of the given comment plus 1
|
// 3. Add comment with level of the given comment plus 1
|
||||||
|
|
||||||
var curComment = comment;
|
let curComment = comment;
|
||||||
while ( curComment.replies.length ) {
|
while ( curComment.replies.length ) {
|
||||||
curComment = curComment.replies[ curComment.replies.length - 1 ];
|
curComment = curComment.replies[ curComment.replies.length - 1 ];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tag names for lists and items we're going to insert
|
// Tag names for lists and items we're going to insert
|
||||||
var itemType;
|
let itemType;
|
||||||
if ( replyIndentation === 'invisible' ) {
|
if ( replyIndentation === 'invisible' ) {
|
||||||
itemType = 'dd';
|
itemType = 'dd';
|
||||||
} else if ( replyIndentation === 'bullet' ) {
|
} else if ( replyIndentation === 'bullet' ) {
|
||||||
|
@ -76,16 +76,16 @@ function addListItem( comment, replyIndentation ) {
|
||||||
} else {
|
} else {
|
||||||
throw new Error( "Invalid reply indentation syntax '" + replyIndentation + "'" );
|
throw new Error( "Invalid reply indentation syntax '" + replyIndentation + "'" );
|
||||||
}
|
}
|
||||||
var listType = listTypeMap[ itemType ];
|
const listType = listTypeMap[ itemType ];
|
||||||
|
|
||||||
var desiredLevel = comment.level + 1;
|
const desiredLevel = comment.level + 1;
|
||||||
var target = curComment.range.endContainer;
|
let target = curComment.range.endContainer;
|
||||||
|
|
||||||
// target is a text node or an inline element at the end of a "paragraph" (not necessarily paragraph node).
|
// 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.
|
// 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
|
// 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.
|
// 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 ) {
|
while ( target.parentNode !== parent ) {
|
||||||
target = target.parentNode;
|
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.
|
// 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
|
// 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.
|
// 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;
|
curComment.rootNode;
|
||||||
var covered = utils.getFullyCoveredSiblings( curComment, excludedWrapper );
|
const covered = utils.getFullyCoveredSiblings( curComment, excludedWrapper );
|
||||||
if ( curComment.level === 1 && covered ) {
|
if ( curComment.level === 1 && covered ) {
|
||||||
target = covered[ covered.length - 1 ];
|
target = covered[ covered.length - 1 ];
|
||||||
parent = target.parentNode;
|
parent = target.parentNode;
|
||||||
|
@ -105,7 +105,7 @@ function addListItem( comment, replyIndentation ) {
|
||||||
|
|
||||||
// If the comment is in a transclusion, insert replies after the transclusion. (T313100)
|
// 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.
|
// 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 ) {
|
if ( transclusionNode ) {
|
||||||
while (
|
while (
|
||||||
transclusionNode.nextSibling &&
|
transclusionNode.nextSibling &&
|
||||||
|
@ -142,9 +142,9 @@ function addListItem( comment, replyIndentation ) {
|
||||||
|
|
||||||
// Instead of just using curComment.level, consider indentation of lists within the
|
// Instead of just using curComment.level, consider indentation of lists within the
|
||||||
// comment (T252702)
|
// 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 ) {
|
if ( desiredLevel === 1 ) {
|
||||||
// Special handling for top-level comments
|
// Special handling for top-level comments
|
||||||
item = target.ownerDocument.createElement( 'div' );
|
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,
|
// 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)
|
// 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.
|
// Skip over comments and whitespace, but only update target when skipping past comments.
|
||||||
var pointer = target;
|
let pointer = target;
|
||||||
while (
|
while (
|
||||||
pointer.nextSibling && (
|
pointer.nextSibling && (
|
||||||
utils.isRenderingTransparentNode( pointer.nextSibling ) ||
|
utils.isRenderingTransparentNode( pointer.nextSibling ) ||
|
||||||
|
@ -196,7 +196,7 @@ function addListItem( comment, replyIndentation ) {
|
||||||
} else {
|
} else {
|
||||||
// Split the ancestor nodes after the target to decrease nesting.
|
// Split the ancestor nodes after the target to decrease nesting.
|
||||||
|
|
||||||
var newNode;
|
let newNode;
|
||||||
do {
|
do {
|
||||||
if ( !target || !parent ) {
|
if ( !target || !parent ) {
|
||||||
throw new Error( 'Can not decrease nesting any more' );
|
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
|
* @param {DocumentFragment|null} fragment Containing document fragment if list has no parent
|
||||||
*/
|
*/
|
||||||
function unwrapList( list, fragment ) {
|
function unwrapList( list, fragment ) {
|
||||||
var doc = list.ownerDocument,
|
let doc = list.ownerDocument,
|
||||||
container = fragment || list.parentNode,
|
container = fragment || list.parentNode,
|
||||||
referenceNode = list;
|
referenceNode = list;
|
||||||
|
|
||||||
|
@ -330,11 +330,11 @@ function unwrapList( list, fragment ) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var insertBefore;
|
let insertBefore;
|
||||||
while ( list.firstChild ) {
|
while ( list.firstChild ) {
|
||||||
if ( list.firstChild.nodeType === Node.ELEMENT_NODE ) {
|
if ( list.firstChild.nodeType === Node.ELEMENT_NODE ) {
|
||||||
// Move <dd> contents to <p>
|
// Move <dd> contents to <p>
|
||||||
var p = doc.createElement( 'p' );
|
let p = doc.createElement( 'p' );
|
||||||
while ( list.firstChild.firstChild ) {
|
while ( list.firstChild.firstChild ) {
|
||||||
// If contents is a block element, place outside the paragraph
|
// If contents is a block element, place outside the paragraph
|
||||||
// and start a new paragraph after
|
// and start a new paragraph after
|
||||||
|
@ -375,7 +375,7 @@ function unwrapList( list, fragment ) {
|
||||||
* @return {HTMLElement}
|
* @return {HTMLElement}
|
||||||
*/
|
*/
|
||||||
function addSiblingListItem( previousItem ) {
|
function addSiblingListItem( previousItem ) {
|
||||||
var listItem = previousItem.ownerDocument.createElement( previousItem.tagName );
|
const listItem = previousItem.ownerDocument.createElement( previousItem.tagName );
|
||||||
previousItem.parentNode.insertBefore( listItem, previousItem.nextSibling );
|
previousItem.parentNode.insertBefore( listItem, previousItem.nextSibling );
|
||||||
return listItem;
|
return listItem;
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,14 +8,14 @@ function init( $container, pageThreads ) {
|
||||||
mw.loader.using( [ 'oojs-ui-widgets', 'oojs-ui.styles.icons-editing-core' ] ).then( () => {
|
mw.loader.using( [ 'oojs-ui-widgets', 'oojs-ui.styles.icons-editing-core' ] ).then( () => {
|
||||||
$container.find( '.ext-discussiontools-init-section-overflowMenuButton' ).each( ( i, button ) => {
|
$container.find( '.ext-discussiontools-init-section-overflowMenuButton' ).each( ( i, button ) => {
|
||||||
// Comment ellipsis
|
// Comment ellipsis
|
||||||
var $threadMarker = $( button ).closest( '[data-mw-thread-id]' );
|
let $threadMarker = $( button ).closest( '[data-mw-thread-id]' );
|
||||||
if ( !$threadMarker.length ) {
|
if ( !$threadMarker.length ) {
|
||||||
// Heading ellipsis
|
// Heading ellipsis
|
||||||
$threadMarker = $( button ).closest( '.ext-discussiontools-init-section' ).find( '[data-mw-thread-id]' );
|
$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,
|
$overlay: true,
|
||||||
menu: {
|
menu: {
|
||||||
classes: [ 'ext-discussiontools-init-section-overflowMenu' ],
|
classes: [ 'ext-discussiontools-init-section-overflowMenu' ],
|
||||||
|
@ -24,13 +24,13 @@ function init( $container, pageThreads ) {
|
||||||
} );
|
} );
|
||||||
|
|
||||||
mw.loader.using( buttonMenu.getData().resourceLoaderModules || [] ).then( () => {
|
mw.loader.using( buttonMenu.getData().resourceLoaderModules || [] ).then( () => {
|
||||||
var itemConfigs = buttonMenu.getData().itemConfigs;
|
const itemConfigs = buttonMenu.getData().itemConfigs;
|
||||||
if ( !itemConfigs ) {
|
if ( !itemConfigs ) {
|
||||||
// We should never have missing itemConfigs, but if this happens, hide the empty menu
|
// We should never have missing itemConfigs, but if this happens, hide the empty menu
|
||||||
buttonMenu.toggle( false );
|
buttonMenu.toggle( false );
|
||||||
return;
|
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().addItems( overflowMenuItemWidgets );
|
||||||
buttonMenu.getMenu().items.forEach( ( menuItem ) => {
|
buttonMenu.getMenu().items.forEach( ( menuItem ) => {
|
||||||
mw.hook( 'discussionToolsOverflowMenuOnAddItem' ).fire( menuItem.getData().id, menuItem, threadItem );
|
mw.hook( 'discussionToolsOverflowMenuOnAddItem' ).fire( menuItem.getData().id, menuItem, threadItem );
|
||||||
|
|
|
@ -5,10 +5,10 @@ function init( $pageContainer ) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function copyLink( link ) {
|
function copyLink( link ) {
|
||||||
var $win = $( window );
|
const $win = $( window );
|
||||||
var scrollTop = $win.scrollTop();
|
const scrollTop = $win.scrollTop();
|
||||||
|
|
||||||
var $tmpInput = $( '<input>' )
|
const $tmpInput = $( '<input>' )
|
||||||
.val( link )
|
.val( link )
|
||||||
.addClass( 'noime' )
|
.addClass( 'noime' )
|
||||||
.css( {
|
.css( {
|
||||||
|
@ -18,7 +18,7 @@ function copyLink( link ) {
|
||||||
.appendTo( 'body' )
|
.appendTo( 'body' )
|
||||||
.trigger( 'focus' );
|
.trigger( 'focus' );
|
||||||
$tmpInput[ 0 ].setSelectionRange( 0, link.length );
|
$tmpInput[ 0 ].setSelectionRange( 0, link.length );
|
||||||
var copied;
|
let copied;
|
||||||
try {
|
try {
|
||||||
copied = document.execCommand( 'copy' );
|
copied = document.execCommand( 'copy' );
|
||||||
} catch ( err ) {
|
} catch ( err ) {
|
||||||
|
|
|
@ -74,7 +74,7 @@ function updateSubscribeButton( button, state ) {
|
||||||
* @return {jQuery.Promise} Promise which resolves after change of state
|
* @return {jQuery.Promise} Promise which resolves after change of state
|
||||||
*/
|
*/
|
||||||
function changeSubscription( title, commentName, subscribe, isNewTopics ) {
|
function changeSubscription( title, commentName, subscribe, isNewTopics ) {
|
||||||
var promise = api.postWithToken( 'csrf', {
|
const promise = api.postWithToken( 'csrf', {
|
||||||
action: 'discussiontoolssubscribe',
|
action: 'discussiontoolssubscribe',
|
||||||
page: title,
|
page: title,
|
||||||
commentname: commentName,
|
commentname: commentName,
|
||||||
|
@ -148,19 +148,19 @@ function initTopicSubscriptions( $container, threadItemSet ) {
|
||||||
$container.find( '.ext-discussiontools-init-section-subscribeButton' ).each( ( i, element ) => {
|
$container.find( '.ext-discussiontools-init-section-subscribeButton' ).each( ( i, element ) => {
|
||||||
// These attributes will be lost when infusing
|
// These attributes will be lost when infusing
|
||||||
// TODO: Could also be fixed by subclassing ButtonWidget in PHP
|
// 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' );
|
.find( '[data-mw-comment-start]' ).attr( 'id' );
|
||||||
var headingItem = threadItemSet.findCommentById( id );
|
const headingItem = threadItemSet.findCommentById( id );
|
||||||
|
|
||||||
if ( !( headingItem instanceof HeadingItem ) ) {
|
if ( !( headingItem instanceof HeadingItem ) ) {
|
||||||
// This should never happen
|
// This should never happen
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var name = headingItem.name;
|
const name = headingItem.name;
|
||||||
var button = OO.ui.infuse( element );
|
const button = OO.ui.infuse( element );
|
||||||
buttonsByName[ name ] = button;
|
buttonsByName[ name ] = button;
|
||||||
|
|
||||||
// Restore data attribute
|
// Restore data attribute
|
||||||
|
@ -168,11 +168,11 @@ function initTopicSubscriptions( $container, threadItemSet ) {
|
||||||
button.$element[ 0 ].setAttribute( 'data-mw-subscribed', String( subscribedStateTemp ) );
|
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', () => {
|
button.on( 'click', () => {
|
||||||
// Get latest subscribedState
|
// Get latest subscribedState
|
||||||
var subscribedState = getSubscribedStateFromElement( button.$element[ 0 ] );
|
const subscribedState = getSubscribedStateFromElement( button.$element[ 0 ] );
|
||||||
|
|
||||||
button.setDisabled( true );
|
button.setDisabled( true );
|
||||||
changeSubscription( title, name, !subscribedState )
|
changeSubscription( title, name, !subscribedState )
|
||||||
|
@ -187,18 +187,18 @@ function initTopicSubscriptions( $container, threadItemSet ) {
|
||||||
|
|
||||||
// Subscription links (no visual enhancements)
|
// Subscription links (no visual enhancements)
|
||||||
$container.find( '.ext-discussiontools-init-section-subscribe-link' ).each( ( i, link ) => {
|
$container.find( '.ext-discussiontools-init-section-subscribe-link' ).each( ( i, link ) => {
|
||||||
var $link = $( link );
|
const $link = $( link );
|
||||||
var id = $link.closest( '.ext-discussiontools-init-section' )
|
const id = $link.closest( '.ext-discussiontools-init-section' )
|
||||||
.find( '[data-mw-comment-start]' ).attr( 'id' );
|
.find( '[data-mw-comment-start]' ).attr( 'id' );
|
||||||
var headingItem = threadItemSet.findCommentById( id );
|
const headingItem = threadItemSet.findCommentById( id );
|
||||||
|
|
||||||
if ( !( headingItem instanceof HeadingItem ) ) {
|
if ( !( headingItem instanceof HeadingItem ) ) {
|
||||||
// This should never happen
|
// This should never happen
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var itemName = headingItem.name;
|
const itemName = headingItem.name;
|
||||||
var title = mw.config.get( 'wgRelevantPageName' ) + '#' + headingItem.getLinkableTitle();
|
const title = mw.config.get( 'wgRelevantPageName' ) + '#' + headingItem.getLinkableTitle();
|
||||||
|
|
||||||
linksByName[ itemName ] = link;
|
linksByName[ itemName ] = link;
|
||||||
|
|
||||||
|
@ -215,7 +215,7 @@ function initTopicSubscriptions( $container, threadItemSet ) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
// Get latest subscribedState
|
// Get latest subscribedState
|
||||||
var subscribedState = getSubscribedStateFromElement( $link[ 0 ] );
|
const subscribedState = getSubscribedStateFromElement( $link[ 0 ] );
|
||||||
|
|
||||||
$link.addClass( 'ext-discussiontools-init-section-subscribe-link-pending' );
|
$link.addClass( 'ext-discussiontools-init-section-subscribe-link-pending' );
|
||||||
changeSubscription( title, itemName, !subscribedState )
|
changeSubscription( title, itemName, !subscribedState )
|
||||||
|
@ -241,7 +241,7 @@ function initTopicSubscriptions( $container, threadItemSet ) {
|
||||||
* page actions like live-preview can still reach this point.
|
* page actions like live-preview can still reach this point.
|
||||||
*/
|
*/
|
||||||
function initNewTopicsSubscription() {
|
function initNewTopicsSubscription() {
|
||||||
var $button, $label, $icon;
|
let $button, $label, $icon;
|
||||||
|
|
||||||
initApi();
|
initApi();
|
||||||
|
|
||||||
|
@ -252,7 +252,7 @@ function initNewTopicsSubscription() {
|
||||||
$icon = $button.find( '.minerva-icon' );
|
$icon = $button.find( '.minerva-icon' );
|
||||||
// HACK: We can't set data-mw-subscribed intially in Minerva, so work it out from the 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
|
// 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 ) );
|
$button.attr( 'data-mw-subscribed', String( initialState ) );
|
||||||
} else {
|
} else {
|
||||||
// eslint-disable-next-line no-jquery/no-global-selector
|
// eslint-disable-next-line no-jquery/no-global-selector
|
||||||
|
@ -261,13 +261,13 @@ function initNewTopicsSubscription() {
|
||||||
$icon = $( [] );
|
$icon = $( [] );
|
||||||
}
|
}
|
||||||
|
|
||||||
var titleObj = mw.Title.newFromText( mw.config.get( 'wgRelevantPageName' ) );
|
const titleObj = mw.Title.newFromText( mw.config.get( 'wgRelevantPageName' ) );
|
||||||
var name = utils.getNewTopicsSubscriptionId( titleObj );
|
const name = utils.getNewTopicsSubscriptionId( titleObj );
|
||||||
|
|
||||||
$button.off( '.mw-dt-topicsubscriptions' ).on( 'click.mw-dt-topicsubscriptions', ( e ) => {
|
$button.off( '.mw-dt-topicsubscriptions' ).on( 'click.mw-dt-topicsubscriptions', ( e ) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
// Get latest subscribedState
|
// Get latest subscribedState
|
||||||
var subscribedState = getSubscribedStateFromElement( $button[ 0 ] );
|
const subscribedState = getSubscribedStateFromElement( $button[ 0 ] );
|
||||||
|
|
||||||
changeSubscription( titleObj.getPrefixedText(), name, !subscribedState, true )
|
changeSubscription( titleObj.getPrefixedText(), name, !subscribedState, true )
|
||||||
.then( ( result ) => {
|
.then( ( result ) => {
|
||||||
|
@ -284,9 +284,9 @@ function initSpecialTopicSubscriptions() {
|
||||||
// Unsubscribe links on special page
|
// Unsubscribe links on special page
|
||||||
// eslint-disable-next-line no-jquery/no-global-selector
|
// eslint-disable-next-line no-jquery/no-global-selector
|
||||||
$( '.ext-discussiontools-special-unsubscribe-button' ).each( ( i, element ) => {
|
$( '.ext-discussiontools-special-unsubscribe-button' ).each( ( i, element ) => {
|
||||||
var button = OO.ui.infuse( element );
|
const button = OO.ui.infuse( element );
|
||||||
var data = button.getData();
|
const data = button.getData();
|
||||||
var subscribedState = STATE_SUBSCRIBED;
|
let subscribedState = STATE_SUBSCRIBED;
|
||||||
|
|
||||||
button.on( 'click', () => {
|
button.on( 'click', () => {
|
||||||
button.setDisabled( true );
|
button.setDisabled( true );
|
||||||
|
@ -311,7 +311,7 @@ function initSpecialTopicSubscriptions() {
|
||||||
* Show the first time popup for auto topic subscriptions, if required
|
* Show the first time popup for auto topic subscriptions, if required
|
||||||
*/
|
*/
|
||||||
function maybeShowFirstTimeAutoTopicSubPopup() {
|
function maybeShowFirstTimeAutoTopicSubPopup() {
|
||||||
var lastHighlightComment = require( './highlighter.js' ).getLastHighlightedPublishedComment();
|
const lastHighlightComment = require( './highlighter.js' ).getLastHighlightedPublishedComment();
|
||||||
|
|
||||||
if ( !lastHighlightComment || seenAutoTopicSubPopup ) {
|
if ( !lastHighlightComment || seenAutoTopicSubPopup ) {
|
||||||
return;
|
return;
|
||||||
|
@ -321,7 +321,7 @@ function maybeShowFirstTimeAutoTopicSubPopup() {
|
||||||
mw.user.options.set( 'discussiontools-seenautotopicsubpopup', '1' );
|
mw.user.options.set( 'discussiontools-seenautotopicsubpopup', '1' );
|
||||||
api.saveOption( 'discussiontools-seenautotopicsubpopup', '1' );
|
api.saveOption( 'discussiontools-seenautotopicsubpopup', '1' );
|
||||||
|
|
||||||
var $popupContent, popup;
|
let $popupContent, popup;
|
||||||
|
|
||||||
function close() {
|
function close() {
|
||||||
popup.$element.removeClass( 'ext-discussiontools-autotopicsubpopup-fadein' );
|
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.
|
// If the topic is already marked as auto-subscribed, there's nothing to do.
|
||||||
// (Except maybe show the first-time popup.)
|
// (Except maybe show the first-time popup.)
|
||||||
// If the topic is marked as having never been subscribed, check if they are auto-subscribed now.
|
// If the topic is marked as having never been subscribed, check if they are auto-subscribed now.
|
||||||
var topicsToCheck = [];
|
const topicsToCheck = [];
|
||||||
var pendingLinks = [];
|
const pendingLinks = [];
|
||||||
var pendingButtons = [];
|
const pendingButtons = [];
|
||||||
for ( var headingName in headingsToUpdate ) {
|
for ( const headingName in headingsToUpdate ) {
|
||||||
var link = linksByName[ headingName ];
|
const link = linksByName[ headingName ];
|
||||||
var button = buttonsByName[ headingName ];
|
const button = buttonsByName[ headingName ];
|
||||||
var subscribedState = getSubscribedStateFromElement( link || button.$element[ 0 ] );
|
const subscribedState = getSubscribedStateFromElement( link || button.$element[ 0 ] );
|
||||||
|
|
||||||
if ( subscribedState === STATE_AUTOSUBSCRIBED ) {
|
if ( subscribedState === STATE_AUTOSUBSCRIBED ) {
|
||||||
maybeShowFirstTimeAutoTopicSubPopup();
|
maybeShowFirstTimeAutoTopicSubPopup();
|
||||||
|
@ -458,7 +458,7 @@ function updateSubscriptionStates( $container, headingsToUpdate ) {
|
||||||
// updateSubscriptionStates() method is only called if we're really expecting one to be there.
|
// 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
|
// (There are certainly neater ways to implement this, involving push notifications or at
|
||||||
// least long-polling or something. But this is the simplest one!)
|
// least long-polling or something. But this is the simplest one!)
|
||||||
var wait = $.Deferred();
|
const wait = $.Deferred();
|
||||||
setTimeout( wait.resolve, 5000 );
|
setTimeout( wait.resolve, 5000 );
|
||||||
return wait.then( () => api.get( {
|
return wait.then( () => api.get( {
|
||||||
action: 'discussiontoolsgetsubscriptions',
|
action: 'discussiontoolsgetsubscriptions',
|
||||||
|
@ -468,8 +468,8 @@ function updateSubscriptionStates( $container, headingsToUpdate ) {
|
||||||
return response;
|
return response;
|
||||||
} ).then( ( response ) => {
|
} ).then( ( response ) => {
|
||||||
// Update state of each topic for which there is a subscription
|
// Update state of each topic for which there is a subscription
|
||||||
for ( var subItemName in response.subscriptions ) {
|
for ( const subItemName in response.subscriptions ) {
|
||||||
var state = response.subscriptions[ subItemName ];
|
const state = response.subscriptions[ subItemName ];
|
||||||
if ( linksByName[ subItemName ] ) {
|
if ( linksByName[ subItemName ] ) {
|
||||||
updateSubscribeLink( linksByName[ subItemName ], state );
|
updateSubscribeLink( linksByName[ subItemName ], state );
|
||||||
}
|
}
|
||||||
|
@ -496,8 +496,8 @@ function updateSubscriptionStates( $container, headingsToUpdate ) {
|
||||||
* @param {string} [threadItemId] Just-posted comment ID (or NEW_TOPIC_COMMENT_ID)
|
* @param {string} [threadItemId] Just-posted comment ID (or NEW_TOPIC_COMMENT_ID)
|
||||||
*/
|
*/
|
||||||
function updateAutoSubscriptionStates( $container, threadItemSet, threadItemId ) {
|
function updateAutoSubscriptionStates( $container, threadItemSet, threadItemId ) {
|
||||||
var recentComments = [];
|
const recentComments = [];
|
||||||
var headingsToUpdate = {};
|
const headingsToUpdate = {};
|
||||||
if ( threadItemId ) {
|
if ( threadItemId ) {
|
||||||
// Edited by using the reply tool or new topic tool. Only check the edited topic.
|
// Edited by using the reply tool or new topic tool. Only check the edited topic.
|
||||||
if ( threadItemId === utils.NEW_TOPIC_COMMENT_ID ) {
|
if ( threadItemId === utils.NEW_TOPIC_COMMENT_ID ) {
|
||||||
|
@ -507,7 +507,7 @@ function updateAutoSubscriptionStates( $container, threadItemSet, threadItemId )
|
||||||
}
|
}
|
||||||
} else if ( mw.config.get( 'wgPostEdit' ) ) {
|
} else if ( mw.config.get( 'wgPostEdit' ) ) {
|
||||||
// Edited by using wikitext editor. Check topics with their own comments within last minute.
|
// 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 (
|
if (
|
||||||
threadItemSet.threadItems[ i ] instanceof CommentItem &&
|
threadItemSet.threadItems[ i ] instanceof CommentItem &&
|
||||||
threadItemSet.threadItems[ i ].author === mw.user.getName() &&
|
threadItemSet.threadItems[ i ].author === mw.user.getName() &&
|
||||||
|
@ -518,7 +518,7 @@ function updateAutoSubscriptionStates( $container, threadItemSet, threadItemId )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
recentComments.forEach( ( recentComment ) => {
|
recentComments.forEach( ( recentComment ) => {
|
||||||
var headingItem = recentComment.getSubscribableHeading();
|
const headingItem = recentComment.getSubscribableHeading();
|
||||||
if ( headingItem ) {
|
if ( headingItem ) {
|
||||||
// Use names as object keys to deduplicate if there are multiple comments in a topic.
|
// Use names as object keys to deduplicate if there are multiple comments in a topic.
|
||||||
headingsToUpdate[ headingItem.name ] = headingItem;
|
headingsToUpdate[ headingItem.name ] = headingItem;
|
||||||
|
|
|
@ -21,7 +21,7 @@ var solTransparentLinkRegexp = /(?:^|\s)mw:PageProp\/(?:Category|redirect|Langua
|
||||||
* @return {boolean} Node is considered a rendering-transparent node in Parsoid
|
* @return {boolean} Node is considered a rendering-transparent node in Parsoid
|
||||||
*/
|
*/
|
||||||
function isRenderingTransparentNode( node ) {
|
function isRenderingTransparentNode( node ) {
|
||||||
var nextSibling = node.nextSibling;
|
const nextSibling = node.nextSibling;
|
||||||
return (
|
return (
|
||||||
node.nodeType === Node.COMMENT_NODE ||
|
node.nodeType === Node.COMMENT_NODE ||
|
||||||
node.nodeType === Node.ELEMENT_NODE && (
|
node.nodeType === Node.ELEMENT_NODE && (
|
||||||
|
@ -117,7 +117,7 @@ function isCommentSeparator( node ) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var tagName = node.tagName.toLowerCase();
|
const tagName = node.tagName.toLowerCase();
|
||||||
if ( tagName === 'br' || tagName === 'hr' ) {
|
if ( tagName === 'br' || tagName === 'hr' ) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -130,7 +130,7 @@ function isCommentSeparator( node ) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
var classList = node.classList;
|
const classList = node.classList;
|
||||||
if (
|
if (
|
||||||
// Anything marked as not containing comments
|
// Anything marked as not containing comments
|
||||||
classList.contains( 'mw-notalk' ) ||
|
classList.contains( 'mw-notalk' ) ||
|
||||||
|
@ -174,7 +174,7 @@ function isCommentContent( node ) {
|
||||||
* @return {number} Index in parentNode's childNode list
|
* @return {number} Index in parentNode's childNode list
|
||||||
*/
|
*/
|
||||||
function childIndexOf( child ) {
|
function childIndexOf( child ) {
|
||||||
var i = 0;
|
let i = 0;
|
||||||
while ( ( child = child.previousSibling ) ) {
|
while ( ( child = child.previousSibling ) ) {
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
|
@ -219,7 +219,7 @@ function getTranscludedFromElement( node ) {
|
||||||
node.getAttribute( 'about' ) &&
|
node.getAttribute( 'about' ) &&
|
||||||
/^#mwt\d+$/.test( node.getAttribute( 'about' ) )
|
/^#mwt\d+$/.test( node.getAttribute( 'about' ) )
|
||||||
) {
|
) {
|
||||||
var about = node.getAttribute( 'about' );
|
const about = node.getAttribute( 'about' );
|
||||||
|
|
||||||
// 2.
|
// 2.
|
||||||
while (
|
while (
|
||||||
|
@ -252,7 +252,7 @@ function getTranscludedFromElement( node ) {
|
||||||
*/
|
*/
|
||||||
function getHeadlineNode( heading ) {
|
function getHeadlineNode( heading ) {
|
||||||
// This code assumes that $wgFragmentMode is [ 'html5', 'legacy' ] or [ 'html5' ]
|
// This code assumes that $wgFragmentMode is [ 'html5', 'legacy' ] or [ 'html5' ]
|
||||||
var headline = heading;
|
let headline = heading;
|
||||||
|
|
||||||
if ( headline.hasAttribute( 'data-mw-comment-start' ) ) {
|
if ( headline.hasAttribute( 'data-mw-comment-start' ) ) {
|
||||||
// JS only: Support output from the PHP CommentFormatter
|
// JS only: Support output from the PHP CommentFormatter
|
||||||
|
@ -292,12 +292,12 @@ function htmlTrim( str ) {
|
||||||
* @return {number}
|
* @return {number}
|
||||||
*/
|
*/
|
||||||
function getIndentLevel( node, rootNode ) {
|
function getIndentLevel( node, rootNode ) {
|
||||||
var indent = 0;
|
let indent = 0;
|
||||||
while ( node ) {
|
while ( node ) {
|
||||||
if ( node === rootNode ) {
|
if ( node === rootNode ) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
var tagName = node instanceof HTMLElement ? node.tagName.toLowerCase() : null;
|
const tagName = node instanceof HTMLElement ? node.tagName.toLowerCase() : null;
|
||||||
if ( tagName === 'li' || tagName === 'dd' ) {
|
if ( tagName === 'li' || tagName === 'dd' ) {
|
||||||
indent++;
|
indent++;
|
||||||
}
|
}
|
||||||
|
@ -313,11 +313,11 @@ function getIndentLevel( node, rootNode ) {
|
||||||
* @return {Node[]}
|
* @return {Node[]}
|
||||||
*/
|
*/
|
||||||
function getCoveredSiblings( range ) {
|
function getCoveredSiblings( range ) {
|
||||||
var ancestor = range.commonAncestorContainer;
|
const ancestor = range.commonAncestorContainer;
|
||||||
|
|
||||||
var siblings = ancestor.childNodes;
|
const siblings = ancestor.childNodes;
|
||||||
var start = 0;
|
let start = 0;
|
||||||
var end = siblings.length - 1;
|
let end = siblings.length - 1;
|
||||||
|
|
||||||
// Find first of the siblings that contains the item
|
// Find first of the siblings that contains the item
|
||||||
if ( ancestor === range.startContainer ) {
|
if ( ancestor === range.startContainer ) {
|
||||||
|
@ -350,20 +350,20 @@ function getCoveredSiblings( range ) {
|
||||||
* @return {Node[]|null}
|
* @return {Node[]|null}
|
||||||
*/
|
*/
|
||||||
function getFullyCoveredSiblings( item, excludedAncestorNode ) {
|
function getFullyCoveredSiblings( item, excludedAncestorNode ) {
|
||||||
var siblings = getCoveredSiblings( item.getRange() );
|
let siblings = getCoveredSiblings( item.getRange() );
|
||||||
|
|
||||||
function makeRange( sibs ) {
|
function makeRange( sibs ) {
|
||||||
var range = sibs[ 0 ].ownerDocument.createRange();
|
const range = sibs[ 0 ].ownerDocument.createRange();
|
||||||
range.setStartBefore( sibs[ 0 ] );
|
range.setStartBefore( sibs[ 0 ] );
|
||||||
range.setEndAfter( sibs[ sibs.length - 1 ] );
|
range.setEndAfter( sibs[ sibs.length - 1 ] );
|
||||||
return range;
|
return range;
|
||||||
}
|
}
|
||||||
|
|
||||||
var matches = compareRanges( makeRange( siblings ), item.getRange() ) === 'equal';
|
const matches = compareRanges( makeRange( siblings ), item.getRange() ) === 'equal';
|
||||||
|
|
||||||
if ( matches ) {
|
if ( matches ) {
|
||||||
// If these are all of the children (or the only child), go up one more level
|
// If these are all of the children (or the only child), go up one more level
|
||||||
var parent;
|
let parent;
|
||||||
while (
|
while (
|
||||||
( parent = siblings[ 0 ].parentNode ) &&
|
( parent = siblings[ 0 ].parentNode ) &&
|
||||||
parent !== excludedAncestorNode &&
|
parent !== excludedAncestorNode &&
|
||||||
|
@ -387,18 +387,18 @@ function getTitleFromUrl( url ) {
|
||||||
if ( !url ) {
|
if ( !url ) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
var parsedUrl = new URL( url );
|
const parsedUrl = new URL( url );
|
||||||
if ( parsedUrl.searchParams.get( 'title' ) ) {
|
if ( parsedUrl.searchParams.get( 'title' ) ) {
|
||||||
return parsedUrl.searchParams.get( 'title' );
|
return parsedUrl.searchParams.get( 'title' );
|
||||||
}
|
}
|
||||||
|
|
||||||
// wgArticlePath is site config so is trusted
|
// wgArticlePath is site config so is trusted
|
||||||
// eslint-disable-next-line security/detect-non-literal-regexp
|
// eslint-disable-next-line security/detect-non-literal-regexp
|
||||||
var articlePathRegexp = new RegExp(
|
const articlePathRegexp = new RegExp(
|
||||||
mw.util.escapeRegExp( mw.config.get( 'wgArticlePath' ) )
|
mw.util.escapeRegExp( mw.config.get( 'wgArticlePath' ) )
|
||||||
.replace( '\\$1', '(.*)' )
|
.replace( '\\$1', '(.*)' )
|
||||||
);
|
);
|
||||||
var match;
|
let match;
|
||||||
if ( ( match = parsedUrl.pathname.match( articlePathRegexp ) ) ) {
|
if ( ( match = parsedUrl.pathname.match( articlePathRegexp ) ) ) {
|
||||||
return decodeURIComponent( match[ 1 ] );
|
return decodeURIComponent( match[ 1 ] );
|
||||||
}
|
}
|
||||||
|
@ -420,7 +420,7 @@ function getTitleFromUrl( url ) {
|
||||||
* @return {any} Final return value of the callback
|
* @return {any} Final return value of the callback
|
||||||
*/
|
*/
|
||||||
function linearWalk( node, callback ) {
|
function linearWalk( node, callback ) {
|
||||||
var
|
let
|
||||||
result = null,
|
result = null,
|
||||||
withinNode = node.parentNode,
|
withinNode = node.parentNode,
|
||||||
beforeNode = node;
|
beforeNode = node;
|
||||||
|
@ -449,7 +449,7 @@ function linearWalk( node, callback ) {
|
||||||
* @inheritdoc #linearWalk
|
* @inheritdoc #linearWalk
|
||||||
*/
|
*/
|
||||||
function linearWalkBackwards( node, callback ) {
|
function linearWalkBackwards( node, callback ) {
|
||||||
var
|
let
|
||||||
result = null,
|
result = null,
|
||||||
withinNode = node.parentNode,
|
withinNode = node.parentNode,
|
||||||
beforeNode = node;
|
beforeNode = node;
|
||||||
|
@ -521,10 +521,10 @@ function getRangeLastNode( range ) {
|
||||||
function compareRanges( a, b ) {
|
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.
|
// 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.
|
// Watch out, the constant names are the opposite of what they should be.
|
||||||
var startToStart = a.compareBoundaryPoints( Range.START_TO_START, b );
|
let startToStart = a.compareBoundaryPoints( Range.START_TO_START, b );
|
||||||
var startToEnd = a.compareBoundaryPoints( Range.END_TO_START, b );
|
const startToEnd = a.compareBoundaryPoints( Range.END_TO_START, b );
|
||||||
var endToStart = a.compareBoundaryPoints( Range.START_TO_END, b );
|
const endToStart = a.compareBoundaryPoints( Range.START_TO_END, b );
|
||||||
var endToEnd = a.compareBoundaryPoints( Range.END_TO_END, b );
|
let endToEnd = a.compareBoundaryPoints( Range.END_TO_END, b );
|
||||||
|
|
||||||
// Check for almost equal ranges (boundary points only differing by uninteresting nodes)
|
// Check for almost equal ranges (boundary points only differing by uninteresting nodes)
|
||||||
if (
|
if (
|
||||||
|
@ -580,15 +580,15 @@ function compareRangesAlmostEqualBoundaries( a, b, boundary ) {
|
||||||
// This code is awful, but several attempts to rewrite it made it even worse.
|
// This code is awful, but several attempts to rewrite it made it even worse.
|
||||||
// You're welcome to give it a try.
|
// You're welcome to give it a try.
|
||||||
|
|
||||||
var from = boundary === 'end' ? getRangeLastNode( a ) : getRangeFirstNode( a );
|
const from = boundary === 'end' ? getRangeLastNode( a ) : getRangeFirstNode( a );
|
||||||
var to = boundary === 'end' ? getRangeLastNode( b ) : getRangeFirstNode( b );
|
const to = boundary === 'end' ? getRangeLastNode( b ) : getRangeFirstNode( b );
|
||||||
|
|
||||||
var skipNode = null;
|
let skipNode = null;
|
||||||
if ( boundary === 'end' ) {
|
if ( boundary === 'end' ) {
|
||||||
skipNode = from;
|
skipNode = from;
|
||||||
}
|
}
|
||||||
|
|
||||||
var foundContent = false;
|
let foundContent = false;
|
||||||
linearWalk(
|
linearWalk(
|
||||||
from,
|
from,
|
||||||
( event, n ) => {
|
( event, n ) => {
|
||||||
|
|
|
@ -5,10 +5,10 @@ var
|
||||||
QUnit.module( 'mw.dt.ThreadItem', QUnit.newMwEnvironment() );
|
QUnit.module( 'mw.dt.ThreadItem', QUnit.newMwEnvironment() );
|
||||||
|
|
||||||
QUnit.test( '#getAuthorsBelow/#getThreadItemsBelow', ( assert ) => {
|
QUnit.test( '#getAuthorsBelow/#getThreadItemsBelow', ( assert ) => {
|
||||||
var cases = require( '../cases/authors.json' );
|
const cases = require( '../cases/authors.json' );
|
||||||
|
|
||||||
function newFromJSON( json ) {
|
function newFromJSON( json ) {
|
||||||
var item;
|
let item;
|
||||||
if ( json.type === 'heading' ) {
|
if ( json.type === 'heading' ) {
|
||||||
item = new HeadingItem();
|
item = new HeadingItem();
|
||||||
} else {
|
} else {
|
||||||
|
@ -22,7 +22,7 @@ QUnit.test( '#getAuthorsBelow/#getThreadItemsBelow', ( assert ) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
cases.forEach( ( caseItem ) => {
|
cases.forEach( ( caseItem ) => {
|
||||||
var threadItem = newFromJSON( caseItem.thread ),
|
const threadItem = newFromJSON( caseItem.thread ),
|
||||||
authors = threadItem.getAuthorsBelow();
|
authors = threadItem.getAuthorsBelow();
|
||||||
|
|
||||||
assert.deepEqual(
|
assert.deepEqual(
|
||||||
|
|
|
@ -6,36 +6,36 @@ var
|
||||||
QUnit.module( 'mw.dt.modifier', QUnit.newMwEnvironment() );
|
QUnit.module( 'mw.dt.modifier', QUnit.newMwEnvironment() );
|
||||||
|
|
||||||
require( '../cases/modified.json' ).forEach( ( caseItem ) => {
|
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
|
// 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.
|
// 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 ) {
|
if ( skipTests.indexOf( caseItem.name ) !== -1 ) {
|
||||||
QUnit.skip( testName );
|
QUnit.skip( testName );
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
QUnit.test( testName, ( assert ) => {
|
QUnit.test( testName, ( assert ) => {
|
||||||
var dom = ve.createDocumentFromHtml( require( '../' + caseItem.dom ) ),
|
const dom = ve.createDocumentFromHtml( require( '../' + caseItem.dom ) ),
|
||||||
expected = ve.createDocumentFromHtml( require( '../' + caseItem.expected ) ),
|
expected = ve.createDocumentFromHtml( require( '../' + caseItem.expected ) ),
|
||||||
config = require( caseItem.config ),
|
config = require( caseItem.config ),
|
||||||
data = require( caseItem.data );
|
data = require( caseItem.data );
|
||||||
|
|
||||||
testUtils.overrideMwConfig( config );
|
testUtils.overrideMwConfig( config );
|
||||||
|
|
||||||
var expectedHtml = testUtils.getThreadContainer( expected ).innerHTML;
|
const expectedHtml = testUtils.getThreadContainer( expected ).innerHTML;
|
||||||
var reverseExpectedHtml = testUtils.getThreadContainer( dom ).innerHTML;
|
const reverseExpectedHtml = testUtils.getThreadContainer( dom ).innerHTML;
|
||||||
|
|
||||||
var container = testUtils.getThreadContainer( dom );
|
const container = testUtils.getThreadContainer( dom );
|
||||||
var title = mw.Title.newFromText( caseItem.title );
|
const title = mw.Title.newFromText( caseItem.title );
|
||||||
var threadItemSet = new Parser( data ).parse( container, title );
|
const threadItemSet = new Parser( data ).parse( container, title );
|
||||||
var comments = threadItemSet.getCommentItems();
|
const comments = threadItemSet.getCommentItems();
|
||||||
|
|
||||||
// Add a reply to every comment. Note that this inserts *all* of the replies, unlike the real
|
// 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
|
// thing, which only deals with one at a time. This isn't ideal but resetting everything after
|
||||||
// every reply would be super slow.
|
// every reply would be super slow.
|
||||||
var nodes = [];
|
const nodes = [];
|
||||||
comments.forEach( ( comment ) => {
|
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;
|
node.textContent = 'Reply to ' + comment.id;
|
||||||
nodes.push( node );
|
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:
|
// Uncomment this to get updated content for the "modified HTML" files, for copy/paste:
|
||||||
// console.log( container.innerHTML );
|
// console.log( container.innerHTML );
|
||||||
|
|
||||||
var actualHtml = container.innerHTML;
|
const actualHtml = container.innerHTML;
|
||||||
|
|
||||||
assert.strictEqual(
|
assert.strictEqual(
|
||||||
actualHtml,
|
actualHtml,
|
||||||
|
@ -56,7 +56,7 @@ require( '../cases/modified.json' ).forEach( ( caseItem ) => {
|
||||||
modifier.removeAddedListItem( node );
|
modifier.removeAddedListItem( node );
|
||||||
} );
|
} );
|
||||||
|
|
||||||
var reverseActualHtml = container.innerHTML;
|
const reverseActualHtml = container.innerHTML;
|
||||||
assert.strictEqual(
|
assert.strictEqual(
|
||||||
reverseActualHtml,
|
reverseActualHtml,
|
||||||
reverseExpectedHtml,
|
reverseExpectedHtml,
|
||||||
|
@ -66,26 +66,26 @@ require( '../cases/modified.json' ).forEach( ( caseItem ) => {
|
||||||
} );
|
} );
|
||||||
|
|
||||||
QUnit.test( '#addReplyLink', ( assert ) => {
|
QUnit.test( '#addReplyLink', ( assert ) => {
|
||||||
var cases = require( '../cases/reply.json' );
|
const cases = require( '../cases/reply.json' );
|
||||||
|
|
||||||
cases.forEach( ( caseItem ) => {
|
cases.forEach( ( caseItem ) => {
|
||||||
var dom = ve.createDocumentFromHtml( require( '../' + caseItem.dom ) ),
|
const dom = ve.createDocumentFromHtml( require( '../' + caseItem.dom ) ),
|
||||||
expected = ve.createDocumentFromHtml( require( '../' + caseItem.expected ) ),
|
expected = ve.createDocumentFromHtml( require( '../' + caseItem.expected ) ),
|
||||||
config = require( caseItem.config ),
|
config = require( caseItem.config ),
|
||||||
data = require( caseItem.data );
|
data = require( caseItem.data );
|
||||||
|
|
||||||
testUtils.overrideMwConfig( config );
|
testUtils.overrideMwConfig( config );
|
||||||
|
|
||||||
var expectedHtml = testUtils.getThreadContainer( expected ).innerHTML;
|
const expectedHtml = testUtils.getThreadContainer( expected ).innerHTML;
|
||||||
|
|
||||||
var container = testUtils.getThreadContainer( dom );
|
const container = testUtils.getThreadContainer( dom );
|
||||||
var title = mw.Title.newFromText( caseItem.title );
|
const title = mw.Title.newFromText( caseItem.title );
|
||||||
var threadItemSet = new Parser( data ).parse( container, title );
|
const threadItemSet = new Parser( data ).parse( container, title );
|
||||||
var comments = threadItemSet.getCommentItems();
|
const comments = threadItemSet.getCommentItems();
|
||||||
|
|
||||||
// Add a reply link to every comment.
|
// Add a reply link to every comment.
|
||||||
comments.forEach( ( comment ) => {
|
comments.forEach( ( comment ) => {
|
||||||
var linkNode = document.createElement( 'a' );
|
const linkNode = document.createElement( 'a' );
|
||||||
linkNode.textContent = 'Reply';
|
linkNode.textContent = 'Reply';
|
||||||
linkNode.href = '#';
|
linkNode.href = '#';
|
||||||
modifier.addReplyLink( comment, linkNode );
|
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:
|
// Uncomment this to get updated content for the "reply HTML" files, for copy/paste:
|
||||||
// console.log( container.innerHTML );
|
// console.log( container.innerHTML );
|
||||||
|
|
||||||
var actualHtml = container.innerHTML;
|
const actualHtml = container.innerHTML;
|
||||||
|
|
||||||
assert.strictEqual(
|
assert.strictEqual(
|
||||||
actualHtml,
|
actualHtml,
|
||||||
|
@ -105,10 +105,10 @@ QUnit.test( '#addReplyLink', ( assert ) => {
|
||||||
} );
|
} );
|
||||||
|
|
||||||
QUnit.test( '#unwrapList', ( assert ) => {
|
QUnit.test( '#unwrapList', ( assert ) => {
|
||||||
var cases = require( '../cases/unwrap.json' );
|
const cases = require( '../cases/unwrap.json' );
|
||||||
|
|
||||||
cases.forEach( ( caseItem ) => {
|
cases.forEach( ( caseItem ) => {
|
||||||
var container = document.createElement( 'div' );
|
const container = document.createElement( 'div' );
|
||||||
|
|
||||||
container.innerHTML = caseItem.html;
|
container.innerHTML = caseItem.html;
|
||||||
modifier.unwrapList( container.childNodes[ caseItem.index || 0 ] );
|
modifier.unwrapList( container.childNodes[ caseItem.index || 0 ] );
|
||||||
|
@ -122,7 +122,7 @@ QUnit.test( '#unwrapList', ( assert ) => {
|
||||||
} );
|
} );
|
||||||
|
|
||||||
QUnit.test( 'sanitizeWikitextLinebreaks', ( assert ) => {
|
QUnit.test( 'sanitizeWikitextLinebreaks', ( assert ) => {
|
||||||
var cases = require( '../cases/sanitize-wikitext-linebreaks.json' );
|
const cases = require( '../cases/sanitize-wikitext-linebreaks.json' );
|
||||||
|
|
||||||
cases.forEach( ( caseItem ) => {
|
cases.forEach( ( caseItem ) => {
|
||||||
assert.strictEqual(
|
assert.strictEqual(
|
||||||
|
|
|
@ -6,7 +6,7 @@ var
|
||||||
QUnit.module( 'mw.dt.Parser', QUnit.newMwEnvironment() );
|
QUnit.module( 'mw.dt.Parser', QUnit.newMwEnvironment() );
|
||||||
|
|
||||||
QUnit.test( '#getTimestampRegexp', ( assert ) => {
|
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' ) );
|
parser = new Parser( require( '../data-en.json' ) );
|
||||||
|
|
||||||
cases.forEach( ( caseItem ) => {
|
cases.forEach( ( caseItem ) => {
|
||||||
|
@ -19,11 +19,11 @@ QUnit.test( '#getTimestampRegexp', ( assert ) => {
|
||||||
} );
|
} );
|
||||||
|
|
||||||
QUnit.test( '#getTimestampParser', ( 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' ) );
|
parser = new Parser( require( '../data-en.json' ) );
|
||||||
|
|
||||||
cases.forEach( ( caseItem ) => {
|
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 );
|
expectedDate = moment( caseItem.expected );
|
||||||
|
|
||||||
assert.true(
|
assert.true(
|
||||||
|
@ -34,11 +34,11 @@ QUnit.test( '#getTimestampParser', ( assert ) => {
|
||||||
} );
|
} );
|
||||||
|
|
||||||
QUnit.test( '#getTimestampParser (at DST change)', ( 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' ) );
|
parser = new Parser( require( '../data-en.json' ) );
|
||||||
|
|
||||||
cases.forEach( ( caseItem ) => {
|
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 ),
|
tsParser = parser.getTimestampParser( 'en', caseItem.format, null, caseItem.timezone, caseItem.timezoneAbbrs ),
|
||||||
date = tsParser( caseItem.sample.match( regexp ) ).date;
|
date = tsParser( caseItem.sample.match( regexp ) ).date;
|
||||||
|
|
||||||
|
@ -55,19 +55,19 @@ QUnit.test( '#getTimestampParser (at DST change)', ( assert ) => {
|
||||||
|
|
||||||
require( '../cases/comments.json' ).forEach( ( caseItem ) => {
|
require( '../cases/comments.json' ).forEach( ( caseItem ) => {
|
||||||
|
|
||||||
var testName = '#getThreads (' + caseItem.name + ')';
|
const testName = '#getThreads (' + caseItem.name + ')';
|
||||||
QUnit.test( testName, ( assert ) => {
|
QUnit.test( testName, ( assert ) => {
|
||||||
var dom = ve.createDocumentFromHtml( require( '../' + caseItem.dom ) ),
|
const dom = ve.createDocumentFromHtml( require( '../' + caseItem.dom ) ),
|
||||||
expected = require( caseItem.expected ),
|
expected = require( caseItem.expected ),
|
||||||
config = require( caseItem.config ),
|
config = require( caseItem.config ),
|
||||||
data = require( caseItem.data );
|
data = require( caseItem.data );
|
||||||
|
|
||||||
testUtils.overrideMwConfig( config );
|
testUtils.overrideMwConfig( config );
|
||||||
|
|
||||||
var container = testUtils.getThreadContainer( dom );
|
const container = testUtils.getThreadContainer( dom );
|
||||||
var title = mw.Title.newFromText( caseItem.title );
|
const title = mw.Title.newFromText( caseItem.title );
|
||||||
var threadItemSet = new Parser( data ).parse( container, title );
|
const threadItemSet = new Parser( data ).parse( container, title );
|
||||||
var threads = threadItemSet.getThreads();
|
const threads = threadItemSet.getThreads();
|
||||||
|
|
||||||
threads.forEach( ( thread, i ) => {
|
threads.forEach( ( thread, i ) => {
|
||||||
testUtils.serializeComments( thread, container );
|
testUtils.serializeComments( thread, container );
|
||||||
|
|
|
@ -24,8 +24,8 @@ module.exports.overrideMwConfig = function ( config ) {
|
||||||
module.exports.getThreadContainer = function ( doc ) {
|
module.exports.getThreadContainer = function ( doc ) {
|
||||||
// In tests created from Parsoid output, comments are contained directly in <body>.
|
// 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">.
|
// In tests created from old parser output, comments are contained in <div class="mw-parser-output">.
|
||||||
var body = doc.body;
|
const body = doc.body;
|
||||||
var wrapper = body.querySelector( 'div.mw-parser-output' );
|
const wrapper = body.querySelector( 'div.mw-parser-output' );
|
||||||
return wrapper || body;
|
return wrapper || body;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -38,7 +38,7 @@ module.exports.getThreadContainer = function ( doc ) {
|
||||||
* @return {string} The offset path
|
* @return {string} The offset path
|
||||||
*/
|
*/
|
||||||
function getOffsetPath( ancestor, node, nodeOffset ) {
|
function getOffsetPath( ancestor, node, nodeOffset ) {
|
||||||
var path = [ nodeOffset ];
|
const path = [ nodeOffset ];
|
||||||
while ( node !== ancestor ) {
|
while ( node !== ancestor ) {
|
||||||
if ( node.parentNode === null ) {
|
if ( node.parentNode === null ) {
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
|
|
|
@ -3,26 +3,26 @@ var utils = require( 'ext.discussionTools.init' ).utils;
|
||||||
QUnit.module( 'mw.dt.utils', QUnit.newMwEnvironment() );
|
QUnit.module( 'mw.dt.utils', QUnit.newMwEnvironment() );
|
||||||
|
|
||||||
QUnit.test( '#linearWalk', ( assert ) => {
|
QUnit.test( '#linearWalk', ( assert ) => {
|
||||||
var cases = require( '../cases/linearWalk.json' );
|
const cases = require( '../cases/linearWalk.json' );
|
||||||
|
|
||||||
cases.forEach( ( caseItem ) => {
|
cases.forEach( ( caseItem ) => {
|
||||||
var
|
const
|
||||||
doc = ve.createDocumentFromHtml( require( '../' + caseItem.dom ) ),
|
doc = ve.createDocumentFromHtml( require( '../' + caseItem.dom ) ),
|
||||||
expected = require( caseItem.expected );
|
expected = require( caseItem.expected );
|
||||||
|
|
||||||
var actual = [];
|
const actual = [];
|
||||||
utils.linearWalk( doc, ( event, node ) => {
|
utils.linearWalk( doc, ( event, node ) => {
|
||||||
actual.push( event + ' ' + node.nodeName.toLowerCase() + '(' + node.nodeType + ')' );
|
actual.push( event + ' ' + node.nodeName.toLowerCase() + '(' + node.nodeType + ')' );
|
||||||
} );
|
} );
|
||||||
|
|
||||||
var actualBackwards = [];
|
const actualBackwards = [];
|
||||||
utils.linearWalkBackwards( doc, ( event, node ) => {
|
utils.linearWalkBackwards( doc, ( event, node ) => {
|
||||||
actualBackwards.push( event + ' ' + node.nodeName.toLowerCase() + '(' + node.nodeType + ')' );
|
actualBackwards.push( event + ' ' + node.nodeName.toLowerCase() + '(' + node.nodeType + ')' );
|
||||||
} );
|
} );
|
||||||
|
|
||||||
assert.deepEqual( actual, expected, caseItem.name );
|
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)' );
|
assert.deepEqual( actualBackwards, expectedBackwards, caseItem.name + ' (backwards)' );
|
||||||
|
|
||||||
// Uncomment this to get updated content for the JSON files, for copy/paste:
|
// Uncomment this to get updated content for the JSON files, for copy/paste:
|
||||||
|
|
Loading…
Reference in a new issue