var controller = require( 'ext.discussionTools.init' ).controller, utils = require( 'ext.discussionTools.init' ).utils, logger = require( 'ext.discussionTools.init' ).logger, ModeTabSelectWidget = require( './ModeTabSelectWidget.js' ), ModeTabOptionWidget = require( './ModeTabOptionWidget.js' ), licenseMessages = require( './licenseMessages.json' ), featuresEnabled = mw.config.get( 'wgDiscussionToolsFeaturesEnabled' ) || {}, enable2017Wikitext = featuresEnabled.sourcemodetoolbar; require( './AbandonCommentDialog.js' ); require( './AbandonTopicDialog.js' ); /** * DiscussionTools ReplyWidget class * * @class mw.dt.ReplyWidget * @extends OO.ui.Widget * @constructor * @param {CommentController} commentController Comment controller * @param {CommentDetails} commentDetails * @param {Object} [config] Configuration options * @param {Object} [config.input] Configuration options for the comment input widget */ function ReplyWidget( commentController, commentDetails, config ) { var widget = this; config = config || {}; // Parent constructor ReplyWidget.super.call( this, config ); this.pending = false; this.commentController = commentController; var threadItem = commentController.getThreadItem(); this.commentDetails = commentDetails; this.isNewTopic = !!threadItem.isNewTopic; this.pageName = commentDetails.pageName; this.oldId = commentDetails.oldId; // pageExists can only be false for the new comment tool, so we // don't need to worry about transcluded replies. this.pageExists = mw.config.get( 'wgRelevantArticleId', 0 ) !== 0; var contextNode = utils.closestElement( threadItem.range.endContainer, [ 'dl', 'ul', 'ol' ] ); this.context = contextNode ? contextNode.tagName.toLowerCase() : 'dl'; // TODO: Should storagePrefix include pageName? this.storagePrefix = 'reply/' + threadItem.id; this.storage = controller.storage; // eslint-disable-next-line no-jquery/no-global-selector this.contentDir = $( '#mw-content-text' ).css( 'direction' ); this.hideNewCommentsWarning = false; // Floating position for scroll back buttons: 'top', 'bottom', or null this.floating = null; // Copied from ve.init.Platform.static.isIos this.isIos = /ipad|iphone|ipod/i.test( navigator.userAgent ); this.$window = $( this.getElementWindow() ); this.onWindowScrollThrottled = OO.ui.throttle( this.onWindowScroll.bind( this ), 100 ); this.onViewportChangeThrottled = OO.ui.throttle( this.onViewportChange.bind( this ), 100 ); var inputConfig = $.extend( { placeholder: this.isNewTopic ? mw.msg( 'discussiontools-replywidget-placeholder-newtopic' ) : mw.msg( 'discussiontools-replywidget-placeholder-reply', threadItem.author ), authors: this.isNewTopic ? // No suggestions in new topic tool yet (T277357) [] : // threadItem is a CommentItem when replying threadItem.getHeading().getAuthorsBelow() }, config.input ); this.replyBodyWidget = this.createReplyBodyWidget( inputConfig ); this.replyButtonLabel = this.isNewTopic ? mw.msg( 'discussiontools-replywidget-newtopic' ) : mw.msg( 'discussiontools-replywidget-reply' ); this.replyButton = new OO.ui.ButtonWidget( { flags: [ 'primary', 'progressive' ], label: this.replyButtonLabel, title: this.replyButtonLabel + ' ' + // TODO: Use VE keyboard shortcut generating code ( $.client.profile().platform === 'mac' ? '⌘⏎' : mw.msg( 'visualeditor-key-ctrl' ) + '+' + mw.msg( 'visualeditor-key-enter' ) ) } ); this.cancelButton = new OO.ui.ButtonWidget( { flags: [ 'destructive' ], label: mw.msg( 'discussiontools-replywidget-cancel' ), framed: false, title: mw.msg( 'discussiontools-replywidget-cancel' ) + ' ' + // TODO: Use VE keyboard shortcut generating code ( $.client.profile().platform === 'mac' ? '⎋' : mw.msg( 'visualeditor-key-escape' ) ) } ); this.$headerWrapper = $( '
' ).addClass( 'ext-discussiontools-ui-replyWidget-headerWrapper' ); if ( !OO.ui.isMobile() ) { this.modeTabSelect = new ModeTabSelectWidget( { classes: [ 'ext-discussiontools-ui-replyWidget-modeTabs' ], items: [ new ModeTabOptionWidget( { label: mw.msg( 'discussiontools-replywidget-mode-visual' ), data: 'visual' } ), new ModeTabOptionWidget( { label: mw.msg( 'discussiontools-replywidget-mode-source' ), data: 'source' } ) ], framed: false } ); this.modeTabSelect.$element.attr( 'aria-label', mw.msg( 'visualeditor-mweditmode-tooltip' ) ); // Make the option for the current mode disabled, to make it un-interactable // (we override the styles to make it look as if it was selected) this.modeTabSelect.findItemFromData( this.getMode() ).setDisabled( true ); this.modeTabSelect.connect( this, { choose: 'onModeTabSelectChoose' } ); this.$headerWrapper.append( // Visual mode toolbar attached here by CommentTarget#attachToolbar this.modeTabSelect.$element ); } this.$bodyWrapper = $( '
' ).addClass( 'ext-discussiontools-ui-replyWidget-bodyWrapper' ).append( this.replyBodyWidget.$element ); this.$preview = $( '
' ) .addClass( 'ext-discussiontools-ui-replyWidget-preview' ) .attr( 'data-label', mw.msg( 'discussiontools-replywidget-preview' ) ) // Set preview direction to content direction .attr( 'dir', this.contentDir ); this.$actionsWrapper = $( '
' ).addClass( 'ext-discussiontools-ui-replyWidget-actionsWrapper' ); this.$actions = $( '
' ).addClass( 'ext-discussiontools-ui-replyWidget-actions' ).append( this.cancelButton.$element, this.replyButton.$element ); this.editSummaryInput = new OO.ui.TextInputWidget( { classes: [ 'ext-discussiontools-ui-replyWidget-editSummary' ] } ); mw.widgets.visibleCodePointLimit( this.editSummaryInput, mw.config.get( 'wgCommentCodePointLimit' ) ); this.editSummaryField = new OO.ui.FieldLayout( this.editSummaryInput, { align: 'top', classes: [ 'ext-discussiontools-ui-replyWidget-editSummaryField' ], label: mw.msg( 'discussiontools-replywidget-summary' ) } ); this.advancedToggle = new OO.ui.ButtonWidget( { label: mw.msg( 'discussiontools-replywidget-advanced' ), indicator: 'down', framed: false, flags: [ 'progressive' ], classes: [ 'ext-discussiontools-ui-replyWidget-advancedToggle' ] } ); this.advanced = new OO.ui.MessageWidget( { type: 'message', $content: this.editSummaryField.$element, classes: [ 'ext-discussiontools-ui-replyWidget-advanced' ] } ).toggle( false ).setIcon( '' ); this.$footer = $( '
' ).addClass( 'ext-discussiontools-ui-replyWidget-footer' ); if ( this.pageName !== mw.config.get( 'wgRelevantPageName' ) ) { this.$footer.append( $( '

' ).append( mw.message( 'discussiontools-replywidget-transcluded', this.pageName ).parseDom() ) ); } var $footerLinks = $( '