/*! * VisualEditor UserInterface MWWikitextStringTransferHandler class. * * @copyright See AUTHORS.txt */ /** * Detect an attempt to paste wikitext, and convert it to proper * HTML. * * @class * @extends ve.ui.PlainTextStringTransferHandler * * @constructor * @param {ve.ui.Surface} surface * @param {ve.ui.DataTransferItem} item */ ve.ui.MWWikitextStringTransferHandler = function VeUiMWWikitextStringTransferHandler() { // Parent constructor ve.ui.MWWikitextStringTransferHandler.super.apply( this, arguments ); // Properties this.parsoidRequest = null; }; /* Inheritance */ OO.inheritClass( ve.ui.MWWikitextStringTransferHandler, ve.ui.PlainTextStringTransferHandler ); /* Static properties */ ve.ui.MWWikitextStringTransferHandler.static.name = 'wikitextString'; ve.ui.MWWikitextStringTransferHandler.static.types = ve.ui.MWWikitextStringTransferHandler.super.static.types.concat( [ 'text/x-wiki' ] ); ve.ui.MWWikitextStringTransferHandler.static.handlesPaste = true; ve.ui.MWWikitextStringTransferHandler.static.matchFunction = function ( item ) { const text = item.getAsString(), registry = ve.ui.mwWikitextTransferRegistry; // If the mime type is explicitly wikitext (ie, not plain text), // always accept. if ( item.type === 'text/x-wiki' ) { return true; } // Detect autolink opportunities for magic words. // (The link should be the only contents of paste to match this heuristic) if ( ve.dm.MWMagicLinkNode.static.validateContent( text.trim() ) ) { return true; } // Use a heuristic regexp to find text likely to be wikitext. // This test could be made more sophisticated in the future. for ( const i in registry.registry ) { const rule = registry.registry[ i ]; if ( rule instanceof RegExp ) { if ( registry.registry[ i ].test( text ) ) { return true; } } else if ( text.indexOf( rule ) !== -1 ) { return true; } } return false; }; /** * Create a new document from HTML from Parsoid * * @param {string} html HTML from Parsoid * @param {ve.dm.Document} targetDoc DM document this will eventually be merged with * @return {ve.dm.Document} New document */ ve.ui.MWWikitextStringTransferHandler.static.createDocumentFromParsoidHtml = function ( html, targetDoc ) { const htmlDoc = ve.createDocumentFromHtml( html ); // Strip RESTBase IDs mw.libs.ve.stripRestbaseIds( htmlDoc ); // Strip legacy IDs, for example in section headings mw.libs.ve.stripParsoidFallbackIds( htmlDoc.body ); // Pass an empty object for the second argument (importRules) so that clipboard mode is used // TODO: Fix that API const doc = targetDoc.newFromHtml( htmlDoc, {} ); const data = doc.data.data; const surface = new ve.dm.Surface( doc ); // Filter out auto-generated items, e.g. reference lists // This is done after conversion as the autoGenerated item may contain data // required by other non-autoGenerated items, e.g. reference contents for ( let i = data.length - 1; i >= 0; i-- ) { if ( ve.getProp( data[ i ], 'attributes', 'mw', 'autoGenerated' ) ) { surface.change( ve.dm.TransactionBuilder.static.newFromRemoval( doc, surface.getDocument().getDocumentNode().getNodeFromOffset( i + 1 ).getOuterRange() ) ); } } // Clone elements to avoid about attribute conflicts (T204007) doc.data.cloneElements( true ); return doc; }; /* Methods */ /** * @inheritdoc */ ve.ui.MWWikitextStringTransferHandler.prototype.process = function () { const wikitext = this.item.getAsString(); // We already know how to handle wikitext magic links, no need for the API call if ( ve.dm.MWMagicLinkNode.static.validateContent( wikitext.trim() ) ) { this.resolve( [ { type: 'link/mwMagic', attributes: { content: wikitext.trim() } }, { type: '/link/mwMagic' } ] ); return; } const failure = () => { // There's no DTH fallback handling for failures, so just paste // the raw wikitext if things go wrong. this.resolve( wikitext ); }; // Convert wikitext to html using Parsoid. this.parsoidRequest = ve.init.target.parseWikitextFragment( wikitext, false, this.surface.getModel().getDocument() ); // Don't immediately chain, as this.parsoidRequest must be abortable this.parsoidRequest.then( ( response ) => { if ( ve.getProp( response, 'visualeditor', 'result' ) !== 'success' ) { return failure(); } const doc = this.constructor.static.createDocumentFromParsoidHtml( response.visualeditor.content, this.surface.getModel().getDocument() ); if ( !doc.data.hasContent() ) { return failure(); } this.resolve( doc ); }, failure ); this.createProgress( this.parsoidRequest, ve.msg( 'visualeditor-wikitext-progress' ) ); // Indeterminate progress this.setProgress( null ); }; /** * @inheritdoc */ ve.ui.MWWikitextStringTransferHandler.prototype.abort = function () { // Parent method ve.ui.MWWikitextStringTransferHandler.super.prototype.abort.apply( this, arguments ); if ( this.parsoidRequest ) { this.parsoidRequest.abort(); } }; /* Registration */ ve.ui.dataTransferHandlerFactory.register( ve.ui.MWWikitextStringTransferHandler );