From d0bb5511a5f1ab714167475cf4f4e0938ec3ca04 Mon Sep 17 00:00:00 2001 From: Ed Sanders Date: Fri, 2 Sep 2016 12:06:04 -0700 Subject: [PATCH] Cleanup getDocToSave API and use in source mode Instead of getting wikitext from the HTML, which is probably slower and leads to issues with the whitespace stripping hack, override getDocToSave and get text from the linmod directly. Bug: T144621 Change-Id: I6b6cf16ee8f3720398ba8f5c85e7715c2e68329f --- .../ve.init.mw.DesktopArticleTarget.js | 3 +- ...ve.init.mw.DesktopWikitextArticleTarget.js | 54 +++++++++------ .../ve-mw/init/ve.init.mw.ArticleTarget.js | 68 ++++++++++++------- 3 files changed, 77 insertions(+), 48 deletions(-) diff --git a/modules/ve-mw/init/targets/ve.init.mw.DesktopArticleTarget.js b/modules/ve-mw/init/targets/ve.init.mw.DesktopArticleTarget.js index a174d23d44..a6eb3ce723 100644 --- a/modules/ve-mw/init/targets/ve.init.mw.DesktopArticleTarget.js +++ b/modules/ve-mw/init/targets/ve.init.mw.DesktopArticleTarget.js @@ -641,7 +641,6 @@ ve.init.mw.DesktopArticleTarget.prototype.cancel = function ( trackMechanism ) { } target.clearState(); - target.docToSave = null; target.initialEditSummary = new mw.Uri().query.summary; target.deactivating = false; @@ -1550,7 +1549,7 @@ ve.init.mw.DesktopArticleTarget.prototype.switchToWikitextEditor = function ( di } ); } else { this.serialize( - this.docToSave || this.getSurface().getDom(), + this.getDocToSave(), function ( wikitext ) { ve.track( 'mwedit.abort', { type: 'switchwith', mechanism: 'navigate' } ); target.submitWithSaveFields( { wpDiff: 1, wpAutoSummary: '' }, wikitext ); diff --git a/modules/ve-mw/init/targets/ve.init.mw.DesktopWikitextArticleTarget.js b/modules/ve-mw/init/targets/ve.init.mw.DesktopWikitextArticleTarget.js index cc5d784f58..bf8cf47309 100644 --- a/modules/ve-mw/init/targets/ve.init.mw.DesktopWikitextArticleTarget.js +++ b/modules/ve-mw/init/targets/ve.init.mw.DesktopWikitextArticleTarget.js @@ -47,7 +47,7 @@ ve.init.mw.DesktopWikitextArticleTarget.prototype.switchToWikitextEditor = funct var dataPromise, target = this; - this.serialize( this.docToSave || this.getSurface().getDom() ); + this.serialize( this.getDocToSave() ); dataPromise = this.serializing.then( function ( response ) { // HACK var data = response.visualeditor; @@ -71,16 +71,16 @@ ve.init.mw.DesktopWikitextArticleTarget.prototype.switchToWikitextEditor = funct ve.init.mw.DesktopWikitextArticleTarget.prototype.switchToVisualEditor = function () { var dataPromise; - this.setMode( 'visual' ); - dataPromise = mw.libs.ve.targetLoader.requestParsoidData( this.pageName, this.revid, this.constructor.name, this.edited, - this.getWikitextFromDocument( this.getSurface().getDom() ) + this.getDocToSave() ); + this.setMode( 'visual' ); + this.reloadSurface( dataPromise ); }; @@ -259,33 +259,47 @@ ve.init.mw.DesktopWikitextArticleTarget.prototype.prepareCacheKey = function () // else: No need, just wikitext }; +/** + * @inheritdoc + */ +ve.init.mw.DesktopWikitextArticleTarget.prototype.createDocToSave = function () { + var i, l, text, data; + + if ( this.mode !== 'source' ) { + // Parent method + return ve.init.mw.DesktopWikitextArticleTarget.super.prototype.createDocToSave.apply( this, arguments ); + } + + text = ''; + data = this.getSurface().getModel().getDocument().data.data; + for ( i = 0, l = data.length; i < l; i++ ) { + if ( data[ i ].type === '/paragraph' && data[ i + 1 ].type === 'paragraph' ) { + text += '\n'; + } else if ( !data[ i ].type ) { + text += data[ i ]; + } + } + + return text; +}; + /** * @inheritdoc */ ve.init.mw.DesktopWikitextArticleTarget.prototype.tryWithPreparedCacheKey = function ( doc, options ) { - var data; if ( this.mode === 'source' ) { - data = ve.extendObject( {}, options, { format: 'json' } ); - - data.wikitext = this.getWikitextFromDocument( doc ); - - return new mw.Api().post( data, { contentType: 'multipart/form-data' } ); + return new mw.Api().post( ve.extendObject( {}, options, { + wikitext: doc, + format: 'json' + } ), + { contentType: 'multipart/form-data' } + ); } else { // Parent method return ve.init.mw.DesktopWikitextArticleTarget.super.prototype.tryWithPreparedCacheKey.apply( this, arguments ); } }; -/** - * Get wikitext for the whole document - * - * @param {ve.dm.Document} doc Document - * @return {string} Wikitext - */ -ve.init.mw.DesktopWikitextArticleTarget.prototype.getWikitextFromDocument = function ( doc ) { - return Array.prototype.map.call( doc.body.children, function ( p ) { return p.innerText; } ).join( '\n' ); -}; - /* Registration */ ve.init.mw.targetFactory.register( ve.init.mw.DesktopWikitextArticleTarget ); diff --git a/modules/ve-mw/init/ve.init.mw.ArticleTarget.js b/modules/ve-mw/init/ve.init.mw.ArticleTarget.js index f06966a1b7..fd3956fdfd 100644 --- a/modules/ve-mw/init/ve.init.mw.ArticleTarget.js +++ b/modules/ve-mw/init/ve.init.mw.ArticleTarget.js @@ -893,9 +893,9 @@ ve.init.mw.ArticleTarget.prototype.onSaveDialogReview = function () { this.saveDialog.pushPending(); if ( this.pageExists ) { // Has no callback, handled via target.showChangesDiff - this.showChanges( this.docToSave ); + this.showChanges( this.getDocToSave() ); } else { - this.serialize( this.docToSave, this.onSaveDialogReviewComplete.bind( this ) ); + this.serialize( this.getDocToSave(), this.onSaveDialogReviewComplete.bind( this ) ); } } else { this.saveDialog.swapPanel( 'review' ); @@ -924,7 +924,7 @@ ve.init.mw.ArticleTarget.prototype.onSaveDialogReviewComplete = function ( wikit ve.init.mw.ArticleTarget.prototype.onSaveDialogResolveConflict = function () { // Get Wikitext from the DOM, and set up a submit call when it's done this.serialize( - this.docToSave, + this.getDocToSave(), this.submitWithSaveFields.bind( this, { wpSave: 1 } ) ); }; @@ -946,21 +946,6 @@ ve.init.mw.ArticleTarget.prototype.onSaveDialogRetry = function () { * @fires saveWorkflowEnd */ ve.init.mw.ArticleTarget.prototype.onSaveDialogClose = function () { - var target = this; - - function clear() { - target.docToSave = null; - target.clearPreparedCacheKey(); - } - - // Clear the cached HTML and cache key once the document changes - if ( this.getSurface() ) { - this.getSurface().getModel().getDocument().once( 'transact', clear ); - this.getSurface().once( 'destroy', clear ); - } else { - clear(); - } - this.emit( 'saveWorkflowEnd' ); }; @@ -1036,6 +1021,43 @@ ve.init.mw.ArticleTarget.prototype.clearState = function () { */ ve.init.mw.ArticleTarget.prototype.editSource = null; +/** + * Get a document to save, cached until the surface is modified + * + * The default implementation returns an HTMLDocument, but other targets + * may use a different document model (e.g. plain text for source mode). + * + * @return {Object} Document to save + */ +ve.init.mw.ArticleTarget.prototype.getDocToSave = function () { + var surface; + if ( !this.docToSave ) { + this.docToSave = this.createDocToSave(); + // Cache clearing events + surface = this.getSurface(); + surface.getModel().getDocument().once( 'transact', this.clearDocToSave.bind( this ) ); + surface.once( 'destroy', this.clearDocToSave.bind( this ) ); + } + return this.docToSave; +}; + +/** + * Create a document to save + * + * @return {Object} Document to save + */ +ve.init.mw.ArticleTarget.prototype.createDocToSave = function () { + return this.getSurface().getDom(); +}; + +/** + * Clear the document to save from the cache + */ +ve.init.mw.ArticleTarget.prototype.clearDocToSave = function () { + this.docToSave = null; + this.clearPreparedCacheKey(); +}; + /** * Serialize the current document and store the result in the serialization cache on the server. * @@ -1278,10 +1300,7 @@ ve.init.mw.ArticleTarget.prototype.onSaveDialogSave = function ( saveDeferred ) * @param {Object} saveOptions Save options */ ve.init.mw.ArticleTarget.prototype.startSave = function ( saveOptions ) { - if ( !this.docToSave ) { - this.docToSave = this.getSurface().getDom(); - } - this.save( this.docToSave, saveOptions ); + this.save( this.getDocToSave(), saveOptions ); }; /** @@ -1606,10 +1625,7 @@ ve.init.mw.ArticleTarget.prototype.showSaveDialog = function () { this.emit( 'saveWorkflowBegin' ); // Preload the serialization - if ( !this.docToSave ) { - this.docToSave = this.getSurface().getDom(); - } - this.prepareCacheKey( this.docToSave ); + this.prepareCacheKey( this.getDocToSave() ); // Connect events to save dialog this.getSurface().getDialogs().getWindow( 'mwSave' ).done( function ( win ) {