* Use of __DIR__ instead of dirname( __FILE__ ).
* OutputPage::wrapWikiMsg() no longer supports the 'options' parameter. It was
not used and complicated migration to Message class.
+* Live preview functionality has been improved and moved into the
+ 'mediawiki.action.edit.preview' module. The old 'mediawiki.legacy.preview' module
+ has been removed.
== Compatibility ==
$wgOut->addModules( 'mediawiki.action.edit' );
if ( $wgUser->getOption( 'uselivepreview', false ) ) {
- $wgOut->addModules( 'mediawiki.legacy.preview' );
+ $wgOut->addModules( 'mediawiki.action.edit.preview' );
}
// Bug #19334: textarea jumps when editing articles in IE8
$wgOut->addStyle( 'common/IE80Fixes.css', 'screen', 'IE 8' );
),
'position' => 'top',
),
+ 'mediawiki.action.edit.preview' => array(
+ 'scripts' => 'resources/mediawiki.action/mediawiki.action.edit.preview.js',
+ 'dependencies' => array(
+ 'jquery.form',
+ 'jquery.spinner',
+ ),
+ ),
'mediawiki.action.history' => array(
'scripts' => 'resources/mediawiki.action/mediawiki.action.history.js',
'group' => 'mediawiki.action.history',
'dependencies' => 'mediawiki.legacy.wikibits',
'messages' => array( 'search-mwsuggest-enabled', 'search-mwsuggest-disabled' ),
),
- 'mediawiki.legacy.preview' => array(
- 'scripts' => 'common/preview.js',
- 'remoteBasePath' => $GLOBALS['wgStylePath'],
- 'localBasePath' => $GLOBALS['wgStyleDirectory'],
- 'dependencies' => array(
- 'mediawiki.legacy.wikibits',
- 'jquery.form',
- )
- ),
'mediawiki.legacy.protect' => array(
'scripts' => 'common/protect.js',
'remoteBasePath' => $GLOBALS['wgStylePath'],
--- /dev/null
+/**
+ * Live edit preview.
+ */
+( function ( mw, $ ) {
+
+ /**
+ * @param {jQuery.Event} e
+ */
+ function doLivePreview( e ) {
+ var $wikiPreview, copySelectors, removeSelectors, $copyElements, $spinner,
+ targetUrl, postData, $previewDataHolder;
+
+ e.preventDefault();
+
+ $( mw ).trigger( 'LivePreviewPrepare' );
+
+ $wikiPreview = $( '#wikiPreview' );
+
+ // 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)
+ $wikiPreview.show();
+
+ // 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
+ '#wikiPreview',
+ '#wikiDiff',
+ '#catlinks',
+ '.hiddencats',
+ '#p-lang',
+ // Editing-related
+ '.templatesUsed',
+ '.mw-summary-preview'
+ ];
+ $copyElements = $( copySelectors.join( ',' ) );
+
+ // Not shown during normal preview, to be removed if present
+ removeSelectors = [
+ '.mw-newarticletext'
+ ];
+
+ $( removeSelectors.join( ',' ) ).remove();
+
+ $spinner = $.createSpinner( {
+ size: 'large',
+ type: 'block'
+ });
+ $wikiPreview.before( $spinner );
+ $spinner.css( {
+ position: 'absolute',
+ marginTop: $spinner.height()
+ } );
+ // Make sure preview area is at least as tall as 2x the height of the spinner.
+ // 1x because if its smaller, it will spin behind the edit toolbar.
+ // (this happens on the first preview when editPreview is still empty)
+ // 2x because the spinner has 1x margin top breathing room.
+ $wikiPreview.css( 'minHeight', $spinner.height() * 2 );
+
+ // Can't use fadeTo because it calls show(), and we might want to keep some elements hidden
+ // (e.g. empty #catlinks)
+ $copyElements.animate( {
+ opacity: 0.4
+ }, 'fast' );
+
+ $previewDataHolder = $( '<div>' );
+ targetUrl = $( '#editform' ).attr( 'action' );
+
+ // Gather all the data from the form
+ postData = $( '#editform' ).formToArray();
+ postData.push( {
+ name: e.target.name,
+ value: ''
+ } );
+
+ // Load new preview data.
+ // TODO: This should use the action=parse API instead of loading the entire page
+ // Though that requires figuring out how to conver that raw data into proper HTML.
+ $previewDataHolder.load( targetUrl + ' ' + copySelectors.join( ',' ), postData, function () {
+ var i, $from;
+ // 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] );
+
+ $( copySelectors[i] )
+ .empty()
+ .append( $from.contents() )
+ .attr( 'class', $from.attr( 'class' ) );
+ }
+
+ $spinner.remove();
+ $copyElements.animate( {
+ opacity: 1
+ }, 'fast' );
+
+ $( mw ).trigger( 'LivePreviewDone', [copySelectors] );
+ } );
+ }
+
+ $( document ).ready( function () {
+ // The following elements can change in a preview but are not output
+ // by the server when they're empty until the preview reponse.
+ // TODO: Make the server output these always (in a hidden state), so we don't
+ // 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(
+ $( '<div>' ).prop( 'id', 'p-lang' )
+ );
+ }
+
+ if ( !$( '.mw-summary-preview' ).length ) {
+ $( '.editCheckboxes' ).before(
+ $( '<div>' ).prop( 'className', 'mw-summary-preview' )
+ );
+ }
+
+ if ( !document.getElementById( 'wikiDiff' ) && document.getElementById( 'wikiPreview' ) ) {
+ $( '#wikiPreview' ).after(
+ $( '<div>' ).prop( 'id', 'wikiDiff')
+ );
+ }
+
+ // Make sure diff styles are loaded
+ mw.loader.load( 'mediawiki.action.history.diff' );
+
+ $( document.body ).on( 'click', '#wpPreview, #wpDiff', doLivePreview );
+ } );
+
+}( mediaWiki, jQuery ) );
+++ /dev/null
-/**
- * Live preview script for MediaWiki
- */
-( function( mw, $ ) {
- var doLivePreview = function( e ) {
- e.preventDefault();
-
- $( mw ).trigger( 'LivePreviewPrepare' );
-
- var $wikiPreview = $( '#wikiPreview' );
-
- // this needs to be checked before we unconditionally show the preview
- var overlaySpinner = false;
- if ( $wikiPreview.is( ':visible' ) || $( '.mw-newarticletext:visible' ).length > 0 ) {
- overlaySpinner = true;
- }
-
- // 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)
- $wikiPreview.show();
- // jump to where the preview will appear
- $wikiPreview[0].scrollIntoView();
-
- // list of elements that will be loaded from the preview page
- // elements absent in the preview page (such as .mw-newarticletext) will be cleared using .empty()
- var copySelectors = [
- '#wikiPreview', '#wikiDiff', '#catlinks', '.hiddencats', '#p-lang', // the meat
- '.templatesUsed', '.mw-summary-preview', // editing-related
- '.mw-newarticletext' // it is not shown during normal preview, and looks weird with throbber overlaid
- ];
- var $copyElements = $( copySelectors.join( ',' ) );
-
- var $loadSpinner = $( '<div>' ).addClass( 'mw-ajax-loader' );
- $loadSpinner.css( 'top', '0' ); // move away from header (default is -16px)
-
- // If the preview is already visible, overlay the spinner on top of it.
- if ( overlaySpinner ) {
- $( '#mw-content-text' ).css( 'position', 'relative' ); // FIXME this seems like a bad idea
-
- $loadSpinner.css( {
- 'position': 'absolute',
- 'z-index': '3',
- 'left': '50%',
- 'margin-left': '-16px'
- } );
- }
-
- // fade out the elements and display the throbber
- $( '#mw-content-text' ).prepend( $loadSpinner );
- // we can't use fadeTo because it calls show(), and we might want to keep some elements hidden
- // (e.g. empty #catlinks)
- $copyElements.animate( { 'opacity': 0.4 }, 'fast' );
-
- var $previewDataHolder = $( '<div>' );
- var target = $( '#editform' ).attr( 'action' ) || window.location.href;
-
- // gather all the data from the form
- var postData = $( '#editform' ).formToArray(); // formToArray: from jquery.form
- postData.push( { 'name' : e.target.name, 'value' : '1' } );
-
- // load new preview data
- // FIXME this should use the action=parse API instead of loading the entire page
- $previewDataHolder.load( target + ' ' + copySelectors.join( ',' ), postData, function() {
- // Copy the contents of the specified elements from the loaded page to the real page.
- // Also copy their class attributes.
- for ( var i = 0; i < copySelectors.length; i++ ) {
- var $from = $previewDataHolder.find( copySelectors[i] );
- var $to = $( copySelectors[i] );
-
- $to.empty().append( $from.contents() );
- $to.attr( 'class', $from.attr( 'class' ) );
- }
-
- $loadSpinner.remove();
- $copyElements.animate( { 'opacity': 1 }, 'fast' );
-
- $( mw ).trigger( 'LivePreviewDone', [copySelectors] );
- } );
- };
-
- $( document ).ready( function() {
- // construct the elements we need if they are missing (usually when action=edit)
- // we don't need to hide them, because they are empty when created
-
- // interwiki links
- if ( !document.getElementById( 'p-lang' ) && document.getElementById( 'p-tb' ) ) {
- $( '#p-tb' ).after( $( '<div>' ).attr( 'id', 'p-lang' ) );
- }
-
- // summary preview
- if ( $( '.mw-summary-preview' ).length === 0 ) {
- $( '.editCheckboxes' ).before( $( '<div>' ).addClass( 'mw-summary-preview' ) );
- }
-
- // diff
- if ( !document.getElementById( 'wikiDiff' ) && document.getElementById( 'wikiPreview' ) ) {
- $( '#wikiPreview' ).after( $( '<div>' ).attr( 'id', 'wikiDiff' ) );
- }
-
- // diff styles are usually only loaded during, well, diff, and we might need them
- // (mw.loader takes care of stuff if they happen to be loaded already)
- mw.loader.load( 'mediawiki.action.history.diff' );
-
- $( document.body ).on( 'click', '#wpPreview, #wpDiff', doLivePreview );
- } );
-} )( mediaWiki, jQuery );