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 ) {