Merge "Wikitext mode: Use action=parse for preview"

This commit is contained in:
jenkins-bot 2022-07-16 16:54:30 +00:00 committed by Gerrit Code Review
commit 6f16d02c0c
4 changed files with 77 additions and 164 deletions

View file

@ -953,21 +953,31 @@ ve.init.mw.ArticleTarget.prototype.onSaveDialogPreview = function () {
this.emit( 'savePreview' );
this.saveDialog.pushPending();
var wikitext = this.getDocToSave();
if ( this.sectionTitle && this.sectionTitle.getValue() ) {
wikitext = '== ' + this.sectionTitle.getValue() + ' ==\n\n' + wikitext;
var params = {};
var sectionTitle = this.sectionTitle && this.sectionTitle.getValue();
if ( sectionTitle ) {
params.section = 'new';
params.sectiontitle = sectionTitle;
}
if ( mw.config.get( 'wgUserVariant' ) ) {
params.variant = mw.config.get( 'wgUserVariant' );
}
api.post( {
action: 'visualeditor',
paction: 'parsedoc',
page: this.getPageName(),
wikitext: wikitext,
pst: true
} ).then( function ( response ) {
var baseDoc = target.getSurface().getModel().getDocument().getHtmlDocument();
var doc = target.constructor.static.parseDocument( response.visualeditor.content, 'visual' );
target.saveDialog.showPreview( doc, baseDoc );
api.post( ve.extendObject( params, {
action: 'parse',
title: this.getPageName(),
text: this.getDocToSave(),
pst: true,
preview: true,
sectionpreview: this.section !== null,
disableeditsection: true,
uselang: mw.config.get( 'wgUserLanguage' ),
useskin: mw.config.get( 'skin' ),
mobileformat: OO.ui.isMobile(),
prop: [ 'text', 'categorieshtml', 'displaytitle', 'subtitle', 'modules', 'jsconfigvars' ]
} ) ).then( function ( response ) {
target.saveDialog.showPreview( response );
}, function ( errorCode, details ) {
target.saveDialog.showPreview( target.extractErrorMessages( details ) );
} ).always( function () {

View file

@ -86,7 +86,7 @@
}
}
#catlinks {
.ve-init-mw-desktopArticleTarget-originalContent #catlinks {
cursor: pointer;
&:hover {

View file

@ -214,31 +214,40 @@ mw.libs.ve.fixFragmentLinks = function ( container, docTitle, prefix ) {
var docTitleText = docTitle.getPrefixedText();
prefix = prefix || '';
Array.prototype.forEach.call( container.querySelectorAll( 'a[href*="#"]' ), function ( el ) {
var fragment = new mw.Uri( el.href ).fragment,
targetData = mw.libs.ve.getTargetDataFromHref( el.href, el.ownerDocument );
var fragment = null;
if ( el.getAttribute( 'href' )[ 0 ] === '#' ) {
// Leagcy parser
fragment = el.getAttribute( 'href' ).slice( 1 );
} else {
// Parsoid HTML
var targetData = mw.libs.ve.getTargetDataFromHref( el.href, el.ownerDocument );
if ( targetData.isInternal ) {
var title = mw.Title.newFromText( targetData.title );
if ( title && title.getPrefixedText() === docTitleText ) {
if ( !fragment ) {
// Special case for empty fragment, even if prefix set
el.setAttribute( 'href', '#' );
} else {
if ( prefix ) {
var target = container.querySelector( '#' + $.escapeSelector( fragment ) );
// There may be multiple links to a specific target, so check the target
// hasn't already been fixed (in which case it would be null)
if ( target ) {
target.setAttribute( 'id', prefix + fragment );
target.setAttribute( 'data-mw-id-fixed', '' );
}
}
el.setAttribute( 'href', '#' + prefix + fragment );
if ( targetData.isInternal ) {
var title = mw.Title.newFromText( targetData.title );
if ( title && title.getPrefixedText() === docTitleText ) {
fragment = new mw.Uri( el.href ).fragment;
}
el.removeAttribute( 'target' );
}
}
if ( fragment !== null ) {
if ( !fragment ) {
// Special case for empty fragment, even if prefix set
el.setAttribute( 'href', '#' );
} else {
if ( prefix ) {
var target = container.querySelector( '#' + $.escapeSelector( fragment ) );
// There may be multiple links to a specific target, so check the target
// hasn't already been fixed (in which case it would be null)
if ( target ) {
target.setAttribute( 'id', prefix + fragment );
target.setAttribute( 'data-mw-id-fixed', '' );
}
}
el.setAttribute( 'href', '#' + prefix + fragment );
}
el.removeAttribute( 'target' );
}
} );
// Remove any section heading anchors which weren't fixed above (T218492)
Array.prototype.forEach.call( container.querySelectorAll( 'h1, h2, h3, h4, h5, h6' ), function ( el ) {
@ -314,44 +323,6 @@ mw.libs.ve.getTargetDataFromHref = function ( href, doc ) {
return data;
};
/**
* Expand a string of the form jquery.foo,bar|jquery.ui.baz,quux to
* an array of module names like [ 'jquery.foo', 'jquery.bar',
* 'jquery.ui.baz', 'jquery.ui.quux' ]
*
* Implementation of ResourceLoaderContext::expandModuleNames
* TODO: Consider upstreaming this to MW core.
*
* @param {string} moduleNames Packed module name list
* @return {string[]} Array of module names
*/
mw.libs.ve.expandModuleNames = function ( moduleNames ) {
var modules = [];
moduleNames.split( '|' ).forEach( function ( group ) {
if ( group.indexOf( ',' ) === -1 ) {
// This is not a set of modules in foo.bar,baz notation
// but a single module
modules.push( group );
} else {
// This is a set of modules in foo.bar,baz notation
var matches = group.match( /(.*)\.([^.]*)/ );
if ( !matches ) {
// Prefixless modules, i.e. without dots
modules = modules.concat( group.split( ',' ) );
} else {
// We have a prefix and a bunch of suffixes
var prefix = matches[ 1 ];
var suffixes = matches[ 2 ].split( ',' ); // [ 'bar', 'baz' ]
suffixes.forEach( function ( suffix ) {
modules.push( prefix + '.' + suffix );
} );
}
}
} );
return modules;
};
/**
* Split Parsoid resource name into the href prefix and the page title.
*

View file

@ -204,107 +204,39 @@ ve.ui.MWSaveDialog.prototype.setDiffAndReview = function ( wikitextDiffPromise,
/**
* Set preview content and show preview panel.
*
* @param {HTMLDocument|jQuery} docOrMsg Document to preview, or error message
* @param {HTMLDocument} [baseDoc] Base document against which to normalise links, if document provided
* @param {Object|jQuery} response action=parse API response, or error message
*/
ve.ui.MWSaveDialog.prototype.showPreview = function ( docOrMsg, baseDoc ) {
var dialog = this;
if ( docOrMsg instanceof HTMLDocument ) {
var modules = [];
// Extract required modules for stylesheet tags (avoids re-loading styles)
Array.prototype.forEach.call( docOrMsg.head.querySelectorAll( 'link[rel~=stylesheet]' ), function ( link ) {
var uri = new mw.Uri( link.href );
if ( uri.query.modules ) {
modules = modules.concat( mw.libs.ve.expandModuleNames( uri.query.modules ) );
}
} );
// Remove skin-specific modules (T187075 / T185284)
modules = modules.filter( function ( module ) {
return !/^(skins|mediawiki\.skinning)\./.test( module );
} );
mw.loader.using( modules );
var body = docOrMsg.body;
var categories = [];
// Take a snapshot of all categories
Array.prototype.forEach.call( body.querySelectorAll( 'link[rel~="mw:PageProp/Category"]' ), function ( element ) {
categories.push( ve.dm.nodeFactory.createFromElement( ve.dm.MWCategoryMetaItem.static.toDataElement( [ element ] ) ) );
} );
// Import body to current document, then resolve attributes against original document (parseDocument called #fixBase)
document.adoptNode( body );
// TODO: This code is very similar to ve.ui.PreviewElement+ve.ui.MWPreviewElement
ve.resolveAttributes( body, docOrMsg, ve.dm.Converter.static.computedAttributes );
// Document title will only be set if wikitext contains {{DISPLAYTITLE}}
if ( docOrMsg.title ) {
// HACK: Parse title as it can contain basic wikitext (T122976)
ve.init.target.getContentApi().post( {
action: 'parse',
title: ve.init.target.getPageName(),
prop: 'displaytitle',
text: '{{DISPLAYTITLE:' + docOrMsg.title + '}}\n'
} ).then( function ( response ) {
if ( ve.getProp( response, 'parse', 'displaytitle' ) ) {
// eslint-disable-next-line no-jquery/no-html
dialog.$previewHeading.html( response.parse.displaytitle );
}
} );
}
// Redirect
var $redirect = $( [] );
var redirectMeta = body.querySelector( 'link[rel="mw:PageProp/redirect"]' );
if ( redirectMeta ) {
$redirect = ve.init.mw.ArticleTarget.static.buildRedirectMsg(
mw.libs.ve.getTargetDataFromHref(
redirectMeta.getAttribute( 'href' ),
document
).title
);
}
// TODO: This won't work with formatted titles (T122976)
this.$previewHeading.text( docOrMsg.title || mw.Title.newFromText( ve.init.target.getPageName() ).getPrefixedText() );
ve.ui.MWSaveDialog.prototype.showPreview = function ( response ) {
if ( response instanceof $ ) {
this.$previewViewer.empty().append(
// eslint-disable-next-line no-jquery/no-append-html
$( '<em>' ).append( response )
);
} else {
var data = response.parse;
mw.config.set( data.jsconfigvars );
mw.loader.using( ( data.modules || [] ).concat( data.modulestyles || [] ) );
// eslint-disable-next-line no-jquery/no-html
this.$previewHeading.html( data.displaytitle );
// eslint-disable-next-line no-jquery/no-append-html
this.$previewViewer.empty().append(
$redirect,
// The following classes are used here:
// * mw-content-ltr
// * mw-content-rtl
// eslint-disable-next-line no-jquery/no-append-html
$( '<div>' ).addClass( 'mw-content-' + mw.config.get( 'wgVisualEditor' ).pageLanguageDir ).append(
body.childNodes
)
// eslint-disable-next-line no-jquery/no-html
$( '<div>' ).addClass( 'mw-content-' + mw.config.get( 'wgVisualEditor' ).pageLanguageDir ).html(
data.text
),
data.categorieshtml
);
ve.targetLinksToNewWindow( this.$previewViewer[ 0 ] );
// Add styles so links render with their appropriate classes
ve.init.platform.linkCache.styleParsoidElements( this.$previewViewer, baseDoc );
mw.libs.ve.fixFragmentLinks( this.$previewViewer[ 0 ], mw.Title.newFromText( ve.init.target.getPageName() ), 'mw-save-preview-' );
var deferred;
if ( categories.length ) {
// If there are categories, we need to render them. This involves
// a delay, since they might be hidden categories.
deferred = ve.init.target.renderCategories( categories ).done( function ( $categories ) {
dialog.$previewViewer.append( $categories );
ve.targetLinksToNewWindow( $categories[ 0 ] );
// Add styles so links render with their appropriate classes
ve.init.platform.linkCache.styleParsoidElements( $categories, baseDoc );
} );
} else {
deferred = ve.createDeferred().resolve();
}
deferred.done( function () {
// Run hooks so other things can alter the document
mw.hook( 'wikipage.content' ).fire( dialog.$previewViewer );
} );
} else if ( docOrMsg instanceof $ ) {
this.$previewViewer.empty().append(
// eslint-disable-next-line no-jquery/no-append-html
$( '<em>' ).append( docOrMsg )
);
// Run hooks so other things can alter the document
mw.hook( 'wikipage.content' ).fire( this.$previewViewer );
}
this.popPending();