Merge "Add "developer tools" preferences section"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Tue, 24 Apr 2018 16:05:03 +0000 (16:05 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Tue, 24 Apr 2018 16:05:04 +0000 (16:05 +0000)
resources/Resources.php
resources/src/mediawiki.special/mediawiki.special.apisandbox.js
resources/src/mediawiki.special/mediawiki.special.preferences.tabs.js
resources/src/mediawiki/mediawiki.editfont.css [deleted file]
resources/src/mediawiki/mediawiki.editfont.less [new file with mode: 0644]

index 2f230f2..6386281 100644 (file)
@@ -1411,7 +1411,7 @@ return [
                'targets' => [ 'desktop', 'mobile' ],
        ],
        'mediawiki.editfont.styles' => [
-               'styles' => 'resources/src/mediawiki/mediawiki.editfont.css',
+               'styles' => 'resources/src/mediawiki/mediawiki.editfont.less',
                'targets' => [ 'desktop', 'mobile' ],
        ],
        'mediawiki.visibleTimeout' => [
index a7470da..009f47b 100644 (file)
                                ApiSandbox.updateUI();
                        }
 
-                       // If the hashchange event exists, use it. Otherwise, fake it.
-                       // And, of course, IE has to be dumb.
-                       if ( 'onhashchange' in window &&
-                               ( document.documentMode === undefined || document.documentMode >= 8 )
-                       ) {
-                               $( window ).on( 'hashchange', ApiSandbox.loadFromHash );
-                       } else {
-                               setInterval( function () {
-                                       if ( oldhash !== location.hash ) {
-                                               ApiSandbox.loadFromHash();
-                                       }
-                               }, 1000 );
-                       }
+                       $( window ).on( 'hashchange', ApiSandbox.loadFromHash );
 
                        $content
                                .empty()
                                deferreds = [],
                                paramsAreForced = !!params,
                                displayParams = {},
+                               tokenWidgets = [],
                                checkPages = [ pages.main ];
 
                        // Blur any focused widget before submit, because
                        params = {};
                        while ( checkPages.length ) {
                                page = checkPages.shift();
-                               deferreds.push( page.apiCheckValid() );
+                               if ( page.tokenWidget ) {
+                                       tokenWidgets.push( page.tokenWidget );
+                               }
+                               deferreds = deferreds.concat( page.apiCheckValid() );
                                page.getQueryParams( params, displayParams );
                                subpages = page.getSubpages();
                                for ( i = 0; i < subpages.length; i++ ) {
                        }
 
                        $.when.apply( $, deferreds ).done( function () {
-                               var formatItems, menu, selectedLabel;
+                               var formatItems, menu, selectedLabel, deferred, actions, errorCount;
+
+                               // Count how many times `value` occurs in `array`.
+                               function countValues( value, array ) {
+                                       var count, i;
+                                       count = 0;
+                                       for ( i = 0; i < array.length; i++ ) {
+                                               if ( array[ i ] === value ) {
+                                                       count++;
+                                               }
+                                       }
+                                       return count;
+                               }
 
-                               if ( $.inArray( false, arguments ) !== -1 ) {
-                                       windowManager.openWindow( 'errorAlert', {
-                                               title: Util.parseMsg( 'apisandbox-submit-invalid-fields-title' ),
-                                               message: Util.parseMsg( 'apisandbox-submit-invalid-fields-message' ),
-                                               actions: [
-                                                       {
-                                                               action: 'accept',
-                                                               label: OO.ui.msg( 'ooui-dialog-process-dismiss' ),
-                                                               flags: 'primary'
+                               errorCount = countValues( false, arguments );
+                               if ( errorCount > 0 ) {
+                                       actions = [
+                                               {
+                                                       action: 'accept',
+                                                       label: OO.ui.msg( 'ooui-dialog-process-dismiss' ),
+                                                       flags: 'primary'
+                                               }
+                                       ];
+                                       if ( tokenWidgets.length ) {
+                                               // Check all token widgets' validity separately
+                                               deferred = $.when.apply( $, tokenWidgets.map( function ( w ) {
+                                                       return w.apiCheckValid();
+                                               } ) );
+
+                                               deferred.done( function () {
+                                                       // If only the tokens are invalid, offer to fix them
+                                                       var tokenErrorCount = countValues( false, arguments );
+                                                       if ( tokenErrorCount === errorCount ) {
+                                                               delete actions[ 0 ].flags;
+                                                               actions.push( {
+                                                                       action: 'fix',
+                                                                       label: mw.message( 'apisandbox-results-fixtoken' ).text(),
+                                                                       flags: 'primary'
+                                                               } );
                                                        }
-                                               ]
+                                               } );
+                                       } else {
+                                               deferred = $.Deferred().resolve();
+                                       }
+                                       deferred.always( function () {
+                                               windowManager.openWindow( 'errorAlert', {
+                                                       title: Util.parseMsg( 'apisandbox-submit-invalid-fields-title' ),
+                                                       message: Util.parseMsg( 'apisandbox-submit-invalid-fields-message' ),
+                                                       actions: actions
+                                               } ).closed.then( function ( data ) {
+                                                       if ( data && data.action === 'fix' ) {
+                                                               ApiSandbox.fixTokenAndResend();
+                                                       }
+                                               } );
                                        } );
                                        return;
                                }
        /**
         * Check that all widgets on the page are in a valid state.
         *
-        * @return {boolean}
+        * @return {jQuery.Promise[]} One promise for each widget, resolved with `false` if invalid
         */
        ApiSandbox.PageLayout.prototype.apiCheckValid = function () {
-               var that = this;
+               var promises, that = this;
 
                if ( this.paramInfo === null ) {
-                       return $.Deferred().resolve( false ).promise();
+                       return [];
                } else {
-                       return $.when.apply( $, $.map( this.widgets, function ( widget ) {
+                       promises = $.map( this.widgets, function ( widget ) {
                                return widget.apiCheckValid();
-                       } ) ).then( function () {
+                       } );
+                       $.when.apply( $, promises ).then( function () {
                                that.apiIsValid = $.inArray( false, arguments ) === -1;
                                if ( that.getOutlineItem() ) {
                                        that.getOutlineItem().setIcon( that.apiIsValid || suppressErrors ? null : 'alert' );
                                                that.apiIsValid || suppressErrors ? '' : mw.message( 'apisandbox-alert-page' ).plain()
                                        );
                                }
-                               return $.Deferred().resolve( that.apiIsValid ).promise();
                        } );
+                       return promises;
                }
        };
 
index dcfad27..0d97d68 100644 (file)
                        }
                }
 
-               // In browsers that support the onhashchange event we will not bind click
-               // handlers and instead let the browser do the default behavior (clicking the
-               // <a href="#.."> will naturally set the hash, handled by onhashchange.
-               // But other things that change the hash will also be caught (e.g. using
-               // the Back and Forward browser navigation).
-               // Note the special check for IE "compatibility" mode.
-               if ( 'onhashchange' in window &&
-                       ( document.documentMode === undefined || document.documentMode >= 8 )
-               ) {
-                       $( window ).on( 'hashchange', function () {
-                               var hash = location.hash;
-                               if ( hash.match( /^#mw-[\w-]+/ ) ) {
-                                       detectHash();
-                               } else if ( hash === '' ) {
-                                       switchPrefTab( 'personal', 'noHash' );
-                               }
-                       } )
-                               // Run the function immediately to select the proper tab on startup.
-                               .trigger( 'hashchange' );
-               // In older browsers we'll bind a click handler as fallback.
-               // We must not have onhashchange *and* the click handlers, otherwise
-               // the click handler calls switchPrefTab() which sets the hash value,
-               // which triggers onhashchange and calls switchPrefTab() again.
-               } else {
-                       $preftoc.on( 'click', 'li a', function ( e ) {
-                               switchPrefTab( $( this ).attr( 'href' ).replace( '#mw-prefsection-', '' ) );
-                               e.preventDefault();
-                       } );
-                       // If we've reloaded the page or followed an open-in-new-window,
-                       // make the selected tab visible.
-                       detectHash();
-               }
+               $( window ).on( 'hashchange', function () {
+                       var hash = location.hash;
+                       if ( hash.match( /^#mw-[\w-]+/ ) ) {
+                               detectHash();
+                       } else if ( hash === '' ) {
+                               switchPrefTab( 'personal', 'noHash' );
+                       }
+               } )
+                       // Run the function immediately to select the proper tab on startup.
+                       .trigger( 'hashchange' );
 
                // Restore the active tab after saving the preferences
                previousTab = mw.storage.session.get( 'mwpreferences-prevTab' );
diff --git a/resources/src/mediawiki/mediawiki.editfont.css b/resources/src/mediawiki/mediawiki.editfont.css
deleted file mode 100644 (file)
index fe7f324..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-/* Edit font preference */
-.mw-editfont-monospace {
-       font-family: monospace, monospace;
-}
-
-.mw-editfont-sans-serif {
-       font-family: sans-serif;
-}
-
-.mw-editfont-serif {
-       font-family: serif;
-}
-
-/* Standardize font size for edit areas using edit-fonts T182320 */
-.mw-editfont-monospace,
-.mw-editfont-sans-serif,
-.mw-editfont-serif {
-       font-size: 13px;
-}
diff --git a/resources/src/mediawiki/mediawiki.editfont.less b/resources/src/mediawiki/mediawiki.editfont.less
new file mode 100644 (file)
index 0000000..b8e127a
--- /dev/null
@@ -0,0 +1,30 @@
+/* Edit font preference */
+.mw-editfont-monospace {
+       font-family: monospace, monospace;
+}
+
+.mw-editfont-sans-serif {
+       font-family: sans-serif;
+}
+
+.mw-editfont-serif {
+       font-family: serif;
+}
+
+/* Standardize font size for edit areas using edit-fonts T182320 */
+.mw-editfont-monospace,
+.mw-editfont-sans-serif,
+.mw-editfont-serif {
+       font-size: 13px;
+
+       /* For OOUI TextInputWidget, the parent <div> element uses normal font size, and only
+          the <textarea>/<input> inside of it has the adjusted font size. This allows the width
+          of the widget and size of icons etc. (which are expressed in ems) to stay the same. */
+       &.oo-ui-textInputWidget {
+               font-size: inherit;
+       }
+
+       > .oo-ui-inputWidget-input {
+               font-size: 13px;
+       }
+}