/*! * VisualEditor MediaWiki Initialization ArticleTarget class. * * @copyright 2011-2019 VisualEditor Team and others; see AUTHORS.txt * @license The MIT License (MIT); see LICENSE.txt */ /* eslint-disable no-jquery/no-global-selector */ /* global EasyDeflate */ /** * Initialization MediaWiki article target. * * @class * @extends ve.init.mw.Target * * @constructor * @param {Object} [config] Configuration options */ ve.init.mw.ArticleTarget = function VeInitMwArticleTarget( config ) { var enableVisualSectionEditing; config = config || {}; config.toolbarConfig = $.extend( { shadow: true, actions: true, floatable: true }, config.toolbarConfig ); // Parent constructor ve.init.mw.ArticleTarget.super.call( this, config ); // 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; enableVisualSectionEditing = mw.config.get( 'wgVisualEditorConfig' ).enableVisualSectionEditing; this.enableVisualSectionEditing = enableVisualSectionEditing === true || enableVisualSectionEditing === this.constructor.static.trackingName; this.toolbarScrollOffset = mw.config.get( 'wgVisualEditorToolbarScrollOffset', 0 ); // A workaround, as default URI does not get updated after pushState (T74334) this.currentUri = new mw.Uri( location.href ); this.section = null; this.sectionTitle = null; this.editSummaryValue = null; this.initialEditSummary = null; this.checkboxFields = null; this.checkboxesByName = null; this.$saveAccessKeyElements = null; // Sometimes we actually don't want to send a useful oldid // if we do, PostEdit will give us a 'page restored' message this.requestedRevId = mw.config.get( 'wgRevisionId' ); this.currentRevisionId = mw.config.get( 'wgCurRevisionId' ); this.revid = this.requestedRevId || this.currentRevisionId; this.edited = false; this.restoring = !!this.requestedRevId && this.requestedRevId !== this.currentRevisionId; this.pageDeletedWarning = false; this.submitUrl = ( new mw.Uri( mw.util.getUrl( this.getPageName() ) ) ) .extend( { action: 'submit', veswitched: 1 } ); this.events = { track: function () {}, trackActivationStart: function () {}, trackActivationComplete: function () {} }; this.welcomeDialog = null; this.welcomeDialogPromise = null; 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 editConflict */ /** * @event save * @param {string} html Rendered page HTML from server * @param {string} categoriesHtml Rendered categories HTML from server * @param {number} newid New revision id, undefined if unchanged * @param {boolean} isRedirect Whether this page is a redirect or not * @param {string} displayTitle What HTML to show as the page title * @param {Object} lastModified Object containing user-formatted date * and time strings, or undefined if we made no change. * @param {string} contentSub HTML to show as the content subtitle * @param {Array} modules The modules to be loaded on the page * @param {Object} jsconfigvars The mw.config values needed on the page * Fired immediately after a save is successfully completed */ /** * @event showChanges */ /** * @event noChanges */ /** * @event saveErrorEmpty * Fired when save API returns no data object */ /** * @event saveErrorSpamBlacklist * Fired when save is considered spam or blacklisted * TODO: Move this to the extension */ /** * @event saveErrorAbuseFilter * Fired when AbuseFilter throws warnings * TODO: Move this to the extension */ /** * @event saveErrorBadToken * @param {boolean} willRetry Whether an automatic retry will occur * Fired on save if we have to fetch a new edit token. * This is mainly for analytical purposes. */ /** * @event saveErrorNewUser * Fired when user is logged in as a new user */ /** * @event saveErrorCaptcha * Fired when saveError indicates captcha field is required * TODO: Move this to the extension */ /** * @event saveErrorUnknown * @param {string} errorMsg Error message shown to the user * Fired for any other type of save error */ /** * @event saveErrorPageDeleted * Fired when user tries to save page that was deleted after opening VE */ /** * @event saveErrorTitleBlacklist * Fired when the user tries to save page in violation of the TitleBlacklist */ /** * @event saveErrorHookAborted * Fired when the user tries to save page in violation of an extension */ /** * @event saveErrorReadOnly * Fired when the user tries to save page but the database is locked */ /** * @event loadError */ /** * @event showChangesError */ /** * @event serializeError */ /** * @event serializeComplete * Fired when serialization is complete */ /* 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 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 ); }; /** * Build DOM for the redirect page subtitle (#redirectsub). * * @return {jQuery} */ ve.init.mw.ArticleTarget.static.buildRedirectSub = function () { // Page subtitle // Compare: Article::view() return $( '' ) .attr( 'id', 'redirectsub' ) .append( mw.message( 'redirectpagesub' ).parseDom() ); }; /** * Build DOM for the redirect page content header (.redirectMsg). * * @param {string} title Redirect target * @return {jQuery} */ ve.init.mw.ArticleTarget.static.buildRedirectMsg = function ( title ) { var $link; $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: Article::getRedirectHeaderHtml() return $( '
' ) .addClass( 'redirectMsg' ) // Hack: This is normally inside #mw-content-text, but we may insert it before, so we need this. .addClass( 'mw-content-' + mw.config.get( 'wgVisualEditor' ).pageLanguageDir ) .append( $( '

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