From 30a2b7123337ae52871ab758eec289b5011c7c72 Mon Sep 17 00:00:00 2001 From: Derk-Jan Hartman Date: Sun, 4 May 2014 18:06:02 +0200 Subject: [PATCH] Promote LivePreview from its experimental state * Now loads modules that are required to view the page * Make use of the API * Removed the old deprecated LivePreview events * Fixes additional margin in changes mode Much of this is inspired by the userscripts ajaxPreview by the users AlexSm and Anomie. As a next step, I would like to add mw.template support to this. Bug: 24134 Bug: 53327 Change-Id: Ia339f43122cfe801913628475c7db88d037ec269 --- languages/i18n/en.json | 2 +- languages/i18n/qqq.json | 2 +- resources/Resources.php | 9 + .../mediawiki.action.edit.preview.js | 188 +++++++++++++----- 4 files changed, 152 insertions(+), 49 deletions(-) diff --git a/languages/i18n/en.json b/languages/i18n/en.json index 341f62634c..db965a5c68 100644 --- a/languages/i18n/en.json +++ b/languages/i18n/en.json @@ -28,7 +28,7 @@ "tog-shownumberswatching": "Show the number of watching users", "tog-oldsig": "Existing signature:", "tog-fancysig": "Treat signature as wikitext (without an automatic link)", - "tog-uselivepreview": "Use live preview (experimental)", + "tog-uselivepreview": "Use live preview", "tog-forceeditsummary": "Prompt me when entering a blank edit summary", "tog-watchlisthideown": "Hide my edits from the watchlist", "tog-watchlisthidebots": "Hide bot edits from the watchlist", diff --git a/languages/i18n/qqq.json b/languages/i18n/qqq.json index b9d586c266..42ec675b06 100644 --- a/languages/i18n/qqq.json +++ b/languages/i18n/qqq.json @@ -192,7 +192,7 @@ "tog-shownumberswatching": "Toggle option used in [[Special:Preferences]], in the section for recent changes. When this option is activated, the entries in recent changes includes the number of users who watch pages. {{Gender}}", "tog-oldsig": "Used in [[Special:Preferences]], tab User profile. {{Gender}}", "tog-fancysig": "In user preferences under the signature box. {{Gender}}", - "tog-uselivepreview": "{{Gender}}\nToggle option used in [[Special:Preferences]].\n\nLive preview is an experimental feature (unavailable by default) to use edit preview without loading the page again.", + "tog-uselivepreview": "{{Gender}}\nToggle option used in [[Special:Preferences]].\n\nLive preview is a feature to use edit preview without loading the page again.", "tog-forceeditsummary": "Toggle option used in [[Special:Preferences]] to force an edit ''{{msg-mw|summary}}''. {{Gender}}", "tog-watchlisthideown": "[[Special:Preferences]], tab 'Watchlist'. Offers user to hide own edits from watchlist. {{Gender}}", "tog-watchlisthidebots": "[[Special:Preferences]], tab 'Watchlist'. Offers user to hide bot edits from watchlist. {{Gender}}", diff --git a/resources/Resources.php b/resources/Resources.php index e53ed54802..27b2e5ce08 100644 --- a/resources/Resources.php +++ b/resources/Resources.php @@ -1041,7 +1041,16 @@ return array( 'dependencies' => array( 'jquery.form', 'jquery.spinner', + 'mediawiki.api', 'mediawiki.action.history.diff', + 'mediawiki.util', + 'mediawiki.jqueryMsg', + ), + 'messages' => array( + 'otherlanguages', + 'tooltip-p-lang', + 'summary-preview', + 'parentheses', ), ), 'mediawiki.action.history' => array( diff --git a/resources/src/mediawiki.action/mediawiki.action.edit.preview.js b/resources/src/mediawiki.action/mediawiki.action.edit.preview.js index 6b212c28de..e4f424976c 100644 --- a/resources/src/mediawiki.action/mediawiki.action.edit.preview.js +++ b/resources/src/mediawiki.action/mediawiki.action.edit.preview.js @@ -8,16 +8,16 @@ * @param {jQuery.Event} e */ function doLivePreview( e ) { - var $wikiPreview, $editform, copySelectors, $copyElements, $spinner, - targetUrl, postData, $previewDataHolder; + var isDiff, api, request, postData, copySelectors, section, + $wikiPreview, $wikiDiff, $editform, $copyElements, $spinner; e.preventDefault(); - // Deprecated: Use mw.hook instead - $( mw ).trigger( 'LivePreviewPrepare' ); - + isDiff = ( e.target.name === 'wpDiff' ); $wikiPreview = $( '#wikiPreview' ); + $wikiDiff = $( '#wikiDiff' ); $editform = $( '#editform' ); + section = $editform.find( '[name="wpSection"]' ).val(); // Show #wikiPreview if it's hidden to be able to scroll to it // (if it is hidden, it's also empty, so nothing changes in the rendering) @@ -26,15 +26,12 @@ // Jump to where the preview will appear $wikiPreview[0].scrollIntoView(); - // List of selectors matching elements that we will - // update from from the ajax-loaded preview page. copySelectors = [ // Main '#firstHeading', '#wikiPreview', '#wikiDiff', '#catlinks', - '.hiddencats', '#p-lang', // Editing-related '.templatesUsed', @@ -59,34 +56,112 @@ // (e.g. empty #catlinks) $copyElements.animate( { opacity: 0.4 }, 'fast' ); - $previewDataHolder = $( '
' ); - targetUrl = $editform.attr( 'action' ); - targetUrl += targetUrl.indexOf( '?' ) !== -1 ? '&' : '?'; - targetUrl += $.param( { - debug: mw.config.get( 'debug' ), + api = new mw.Api(); + postData = { + action: 'parse', uselang: mw.config.get( 'wgUserLanguage' ), - useskin: mw.config.get( 'skin' ) - } ); + title: mw.config.get( 'wgPageName' ), + text: $editform.find( '#wpTextbox1' ).val(), + summary: $editform.find( '#wpSummary' ).val() + }; - // Gather all the data from the form - postData = $editform.formToArray(); - postData.push( { - name: e.target.name, - value: '' - } ); + if ( isDiff ) { + $wikiPreview.hide(); - // Load new preview data. - // TODO: This should use the action=parse API instead of loading the entire page, - // although that requires figuring out how to convert that raw data into proper HTML. - $previewDataHolder.load( targetUrl + ' ' + copySelectors.join( ',' ), postData, function () { - var i, $from, $next, $parent; + // First PST the input, then diff it + postData.onlypst = ''; + request = api.post( postData ); + request.done( function ( response ) { + var postData; + postData = { + action: 'query', + indexpageids: '', + prop: 'revisions', + titles: mw.config.get( 'wgPageName' ), + rvdifftotext: response.parse.text['*'], + rvprop: '' + }; + if ( section !== '' ) { + postData.rvsection = section; + } + return api.post( postData ).done( function ( result2 ) { + try { + var diffHtml = result2.query.pages[result2.query.pageids[0]] + .revisions[0].diff['*']; + $wikiDiff.find( 'table.diff tbody' ).html( diffHtml ); + } catch ( e ) { + // "result.blah is undefined" error, ignore + mw.log.warn( e ); + } + $wikiDiff.show(); + } ); + } ); + } else { + $wikiDiff.hide(); + $.extend( postData, { + pst: '', + preview: '', + prop: 'text|displaytitle|modules|categorieshtml|templates|langlinks|limitreporthtml' + } ); + if ( section !== '' ) { + postData.sectionpreview = ''; + } + request = api.post( postData ); + request.done( function ( response ) { + var li, newList, $next, $parent, $list; + if ( response.parse.modules ) { + mw.loader.load( response.parse.modules.concat( + response.parse.modulescripts, + response.parse.modulestyles, + response.parse.modulemessages ) ); + } + if ( response.parse.displaytitle ) { + $( '#firstHeading' ).html( '' + response.parse.displaytitle + '' ); + } + if ( response.parse.categorieshtml ) { + $( '#catlinks' ).replaceWith( response.parse.categorieshtml['*'] ); + } + if ( response.parse.templates ) { + newList = []; + $.each( response.parse.templates, function ( i, template ) { + li = $( '
  • ' ) + .append( $('') + .attr( { + 'href': mw.util.getUrl( template['*'] ), + 'class': ( template.exists !== undefined ? '' : 'new' ) + } ) + .text( template['*'] ) + ); + newList.push( li ); + } ); - // Copy the contents of the specified elements from the loaded page to the real page. - // Also copy their class attributes. - for ( i = 0; i < copySelectors.length; i++ ) { - $from = $previewDataHolder.find( copySelectors[i] ); + $editform.find( '.mw-editfooter-list' ).detach().empty().append( newList ).appendTo( '.templatesUsed' ); + } + if ( response.parse.limitreporthtml ) { + $( '.limitreport' ).html( response.parse.limitreporthtml['*'] ); + } + if ( response.parse.langlinks && mw.config.get( 'skin' ) === 'vector' ) { + newList = []; + $.each( response.parse.langlinks, function ( i, langlink ) { + li = $( '
  • ' ) + .addClass( 'interlanguage-link interwiki-' + langlink.lang ) + .append( $( '' ) + .attr( { + 'href': langlink.url, + 'title': langlink['*'] + ' - ' + langlink.langname, + 'lang': langlink.lang, + 'hreflang': langlink.lang + } ) + .text( langlink.autonym ) + ); + newList.push( li ); + } ); + $list = $( '#p-lang ul' ); + $parent = $list.parent(); + $list.detach().empty().append( newList ).prependTo( $parent ); + } - if ( copySelectors[i] === '#wikiPreview' ) { + if ( response.parse.text['*'] ) { $next = $wikiPreview.next(); // If there is no next node, use parent instead. // Only query parent if needed, false otherwise. @@ -94,9 +169,7 @@ $wikiPreview .detach() - .empty() - .append( $from.contents() ) - .attr( 'class', $from.attr( 'class' ) ); + .html( response.parse.text['*'] ); mw.hook( 'wikipage.content' ).fire( $wikiPreview ); @@ -106,18 +179,28 @@ } else { $next.before( $wikiPreview ); } + $wikiPreview.show(); - } else { - $( copySelectors[i] ) - .empty() - .append( $from.contents() ) - .attr( 'class', $from.attr( 'class' ) ); } + } ); + } + request.done( function ( response ) { + if ( response.parse.parsedsummary ) { + // TODO implement special behavior for section === 'new' + $editform.find( '.mw-summary-preview' ) + .empty() + .append( + mw.message( 'summary-preview' ).parse(), + ' ', + $( '' ).addClass( 'comment' ).html( + // There is no equivalent to rawParams + mw.message( 'parentheses' ).escaped() + .replace( '$1', response.parse.parsedsummary['*'] ) + ) + ); } - - // Deprecated: Use mw.hook instead - $( mw ).trigger( 'LivePreviewDone', [copySelectors] ); - + } ); + request.always( function () { $spinner.remove(); $copyElements.animate( { opacity: 1 @@ -138,9 +221,17 @@ // have to fish and (hopefully) put them in the right place (since skins // can change where they are output). - if ( !document.getElementById( 'p-lang' ) && document.getElementById( 'p-tb' ) ) { - $( '#p-tb' ).after( - $( '
    ' ).attr( 'id', 'p-lang' ) + if ( !document.getElementById( 'p-lang' ) && document.getElementById( 'p-tb' ) && mw.config.get( 'skin' ) === 'vector' ) { + $( '.portal:last' ).after( + $( '
    ' ).attr( { + 'class': 'portal', + 'id': 'p-lang', + 'role': 'navigation', + 'title': mw.msg( 'tooltip-p-lang' ), + 'aria-labelledby': 'p-lang-label' + } ) + .append( $( '

    ' ).attr( 'id', 'p-lang-label' ).text( mw.msg( 'otherlanguages' ) ) ) + .append( $( '
    ' ).addClass( 'body' ).append( '
      ' ) ) ); } @@ -152,7 +243,10 @@ if ( !document.getElementById( 'wikiDiff' ) && document.getElementById( 'wikiPreview' ) ) { $( '#wikiPreview' ).after( - $( '
      ' ).attr( 'id', 'wikiDiff' ) + $( '
      ' ) + .attr( 'id', 'wikiDiff' ) + .html( '' + + '
      ' ) ); } -- 2.20.1