/*! * VisualEditor MediaWiki Initialization ArticleTarget class. * * @copyright See AUTHORS.txt * @license The MIT License (MIT); see LICENSE.txt */ /* eslint-disable no-jquery/no-global-selector */ /** * Initialization MediaWiki article target. * * @class * @extends ve.init.mw.Target * * @constructor * @param {Object} [config] Configuration options * @cfg {Object} [toolbarConfig] * @cfg {boolean} [register=true] */ ve.init.mw.ArticleTarget = function VeInitMwArticleTarget( config ) { config = config || {}; config.toolbarConfig = ve.extendObject( { shadow: true, actions: true, floatable: true }, config.toolbarConfig ); // Parent constructor ve.init.mw.ArticleTarget.super.call( this, config ); // Register if ( config.register !== false ) { // ArticleTargets are never destroyed, but we can't trust ve.init.target to // not get overridden by other targets that may get created on the page. ve.init.articleTarget = this; } // Properties this.saveDialog = null; this.saveDeferred = null; this.saveFields = {}; this.docToSave = null; this.originalDmDocPromise = null; this.originalHtml = null; this.toolbarSaveButton = null; this.pageExists = mw.config.get( 'wgRelevantArticleId', 0 ) !== 0; const enableVisualSectionEditing = mw.config.get( 'wgVisualEditorConfig' ).enableVisualSectionEditing; this.enableVisualSectionEditing = enableVisualSectionEditing === true || enableVisualSectionEditing === this.constructor.static.trackingName; this.toolbarScrollOffset = mw.config.get( 'wgVisualEditorToolbarScrollOffset', 0 ); this.currentUrl = new URL( location.href ); this.section = null; this.visibleSection = null; this.visibleSectionOffset = null; this.sectionTitle = null; this.editSummaryValue = null; this.initialEditSummary = null; this.initialCheckboxes = {}; this.viewUrl = new URL( mw.util.getUrl( this.getPageName() ), location.href ); this.isViewPage = ( mw.config.get( 'wgAction' ) === 'view' && !this.currentUrl.searchParams.has( 'diff' ) ); this.copyrightWarning = null; this.checkboxFields = null; this.checkboxesByName = null; this.$saveAccessKeyElements = null; this.$editableContent = this.getEditableContent(); // Sometimes we actually don't want to send a useful oldid // if we do, PostEdit will give us a 'page restored' message // Use undefined instead of 0 for new documents (T262838) this.requestedRevId = mw.config.get( 'wgEditLatestRevision' ) ? mw.config.get( 'wgCurRevisionId' ) : mw.config.get( 'wgRevisionId' ) || undefined; this.currentRevisionId = mw.config.get( 'wgCurRevisionId' ) || undefined; this.revid = this.requestedRevId || this.currentRevisionId; this.edited = false; this.restoring = !!this.requestedRevId && this.requestedRevId !== this.currentRevisionId; this.pageDeletedWarning = false; this.events = { track: () => {}, trackActivationStart: () => {}, trackActivationComplete: () => {} }; this.preparedCacheKeyPromise = null; this.clearState(); // Initialization this.$element.addClass( 've-init-mw-articleTarget' ); }; /* Inheritance */ OO.inheritClass( ve.init.mw.ArticleTarget, ve.init.mw.Target ); /* Events */ /** * @event ve.init.mw.ArticleTarget#save * @param {Object} data Save data from the API, see ve.init.mw.ArticleTarget#saveComplete * Fired immediately after a save is successfully completed */ /** * @event ve.init.mw.ArticleTarget#savePreview */ /** * @event ve.init.mw.ArticleTarget#saveReview */ /** * @event ve.init.mw.ArticleTarget#saveInitiated */ /** * @event ve.init.mw.ArticleTarget#saveWorkflowBegin */ /** * @event ve.init.mw.ArticleTarget#showChanges */ /** * @event ve.init.mw.ArticleTarget#noChanges */ /** * @event ve.init.mw.ArticleTarget#saveError * @param {string} code Error code */ /** * @event ve.init.mw.ArticleTarget#loadError */ /** * @event ve.init.mw.ArticleTarget#showChangesError */ /** * @event ve.init.mw.ArticleTarget#serializeError */ /** * Fired when serialization is complete * * @event ve.init.mw.ArticleTarget#serializeComplete */ /* Static Properties */ /** * @inheritdoc */ ve.init.mw.ArticleTarget.static.name = 'article'; /** * Tracking name of target class. Used by ArticleTargetEvents to identify which target we are tracking. * * @static * @property {string} * @inheritable */ ve.init.mw.ArticleTarget.static.trackingName = 'mwTarget'; /** * @inheritdoc */ ve.init.mw.ArticleTarget.static.integrationType = 'page'; /** * @inheritdoc */ ve.init.mw.ArticleTarget.static.platformType = 'other'; /** * @inheritdoc */ ve.init.mw.ArticleTarget.static.documentCommands = ve.init.mw.ArticleTarget.super.static.documentCommands.concat( [ // Make help dialog triggerable from anywhere 'commandHelp', // Make save commands triggerable from anywhere 'showSave', 'showChanges', 'showPreview', 'showMinoredit', 'showWatchthis' ] ); /* Static methods */ /** * @inheritdoc */ ve.init.mw.ArticleTarget.static.parseDocument = function ( documentString, mode, section, onlySection ) { // Add trailing linebreak to non-empty wikitext documents for consistency // with old editor and usability. Will be stripped on save. T156609 if ( mode === 'source' && documentString ) { documentString += '\n'; } // Parent method return ve.init.mw.ArticleTarget.super.static.parseDocument.call( this, documentString, mode, section, onlySection ); }; /** * Get the editable part of the page * * @return {jQuery} Editable DOM selection */ ve.init.mw.ArticleTarget.prototype.getEditableContent = function () { return $( '#mw-content-text' ); }; /** * Build DOM for the redirect page subtitle (#redirectsub). * * @return {jQuery} */ ve.init.mw.ArticleTarget.static.buildRedirectSub = function () { const $subMsg = mw.message( 'redirectpagesub' ).parseDom(); // Page subtitle // Compare: Article::view() return $( '' ) .attr( 'id', 'redirectsub' ) .append( $subMsg ); }; /** * Build DOM for the redirect page content header (.redirectMsg). * * @param {string} title Redirect target * @return {jQuery} */ ve.init.mw.ArticleTarget.static.buildRedirectMsg = function ( title ) { const $link = $( '' ) .attr( { href: mw.Title.newFromText( title ).getUrl(), title: mw.msg( 'visualeditor-redirect-description', title ) } ) .text( title ); ve.init.platform.linkCache.styleElement( title, $link ); // Page content header // Compare: LinkRenderer::makeRedirectHeader() return $( '
' ) .addClass( 'redirectMsg' ) // Hack: This is normally inside #mw-content-text, but we may insert it before, so we need this. // The following classes are used here: // * mw-content-ltr // * mw-content-rtl .addClass( 'mw-content-' + mw.config.get( 'wgVisualEditor' ).pageLanguageDir ) .append( $( '

' ).text( mw.msg( 'redirectto' ) ), $( '