HTMLForm: Refactor loading of modules required to infuse fields
authorBartosz Dziewoński <matma.rex@gmail.com>
Sun, 31 Jul 2016 14:56:23 +0000 (16:56 +0200)
committerJforrester <jforrester@wikimedia.org>
Mon, 22 Aug 2016 17:35:35 +0000 (17:35 +0000)
Rather than have a master list in autoinfuse.js (duplicated in
hide-if.js), we put this information in each field class and put it
in the generated HTML as a separate 'data-' attribute. This also
allows new fields defined by extensions to be correctly autoinfused.

Change-Id: I3da75706209cbc16b19cc3f02b355e58ca75fec9

includes/htmlform/HTMLFormElement.php
includes/htmlform/HTMLFormField.php
includes/htmlform/fields/HTMLSelectNamespace.php
includes/htmlform/fields/HTMLTitleTextField.php
includes/htmlform/fields/HTMLUserTextField.php
resources/src/mediawiki/htmlform/autoinfuse.js
resources/src/mediawiki/htmlform/hide-if.js

index e553218..089213c 100644 (file)
@@ -9,15 +9,23 @@
 trait HTMLFormElement {
 
        protected $hideIf = null;
+       protected $modules = null;
 
        public function initializeHTMLFormElement( array $config = [] ) {
                // Properties
                $this->hideIf = isset( $config['hideIf'] ) ? $config['hideIf'] : null;
+               $this->modules = isset( $config['modules'] ) ? $config['modules'] : [];
 
                // Initialization
                if ( $this->hideIf ) {
                        $this->addClasses( [ 'mw-htmlform-hide-if' ] );
                }
+               if ( $this->modules ) {
+                       // JS code must be able to read this before infusing (before OOjs UI is even loaded),
+                       // so we put this in a separate attribute (not with the rest of the config).
+                       // And it's not needed anymore after infusing, so we don't put it in JS config at all.
+                       $this->setAttributes( [ 'data-mw-modules' => implode( ',', $this->modules ) ] );
+               }
                $this->registerConfigCallback( function( &$config ) {
                        if ( $this->hideIf !== null ) {
                                $config['hideIf'] = $this->hideIf;
index 3319d3b..dd53f5e 100644 (file)
@@ -626,8 +626,10 @@ abstract class HTMLFormField {
                        'infusable' => $infusable,
                ];
 
+               $preloadModules = false;
+
                if ( $infusable && $this->shouldInfuseOOUI() ) {
-                       $this->mParent->getOutput()->addModules( 'mediawiki.htmlform.ooui' );
+                       $preloadModules = true;
                        $config['classes'][] = 'mw-htmlform-field-autoinfuse';
                }
 
@@ -638,10 +640,17 @@ abstract class HTMLFormField {
                }
 
                if ( $this->mHideIf ) {
-                       $this->mParent->getOutput()->addModules( 'mediawiki.htmlform.ooui' );
+                       $preloadModules = true;
                        $config['hideIf'] = $this->mHideIf;
                }
 
+               $config['modules'] = $this->getOOUIModules();
+
+               if ( $preloadModules ) {
+                       $this->mParent->getOutput()->addModules( 'mediawiki.htmlform.ooui' );
+                       $this->mParent->getOutput()->addModules( $this->getOOUIModules() );
+               }
+
                return $this->getFieldLayoutOOUI( $inputField, $config );
        }
 
@@ -677,6 +686,16 @@ abstract class HTMLFormField {
                return $this->getHelpText() !== null;
        }
 
+       /**
+        * Get the list of extra ResourceLoader modules which must be loaded client-side before it's
+        * possible to infuse this field's OOjs UI widget.
+        *
+        * @return string[]
+        */
+       protected function getOOUIModules() {
+               return [];
+       }
+
        /**
         * Get the complete raw fields for the input, including help text,
         * labels, and whatever.
index ffa2500..230790d 100644 (file)
@@ -34,6 +34,11 @@ class HTMLSelectNamespace extends HTMLFormField {
                ] );
        }
 
+       protected function getOOUIModules() {
+               // FIXME: NamespaceInputWidget should be in its own module (probably?)
+               return [ 'mediawiki.widgets' ];
+       }
+
        protected function shouldInfuseOOUI() {
                return true;
        }
index 5d5d765..a15b90e 100644 (file)
@@ -73,7 +73,6 @@ class HTMLTitleTextField extends HTMLTextField {
        }
 
        protected function getInputWidget( $params ) {
-               $this->mParent->getOutput()->addModules( 'mediawiki.widgets' );
                if ( $this->mParams['namespace'] !== false ) {
                        $params['namespace'] = $this->mParams['namespace'];
                }
@@ -85,6 +84,11 @@ class HTMLTitleTextField extends HTMLTextField {
                return true;
        }
 
+       protected function getOOUIModules() {
+               // FIXME: TitleInputWidget should be in its own module
+               return [ 'mediawiki.widgets' ];
+       }
+
        public function getInputHtml( $value ) {
                // add mw-searchInput class to enable search suggestions for non-OOUI, too
                $this->mClass .= 'mw-searchInput';
index f21b53d..14b5e59 100644 (file)
@@ -40,8 +40,6 @@ class HTMLUserTextField extends HTMLTextField {
        }
 
        protected function getInputWidget( $params ) {
-               $this->mParent->getOutput()->addModules( 'mediawiki.widgets.UserInputWidget' );
-
                return new UserInputWidget( $params );
        }
 
@@ -49,6 +47,10 @@ class HTMLUserTextField extends HTMLTextField {
                return true;
        }
 
+       protected function getOOUIModules() {
+               return [ 'mediawiki.widgets.UserInputWidget' ];
+       }
+
        public function getInputHtml( $value ) {
                // add the required module and css class for user suggestions in non-OOUI mode
                $this->mParent->getOutput()->addModules( 'mediawiki.userSuggest' );
index 8efbc69..f2e0f4d 100644 (file)
@@ -2,30 +2,24 @@
  * HTMLForm enhancements:
  * Infuse some OOjs UI HTMLForm fields (those which benefit from always being infused).
  */
-( function ( mw ) {
+( function ( mw, $ ) {
 
        mw.hook( 'htmlform.enhance' ).add( function ( $root ) {
-               var $oouiNodes, modules;
+               var $oouiNodes, modules, extraModules;
 
                $oouiNodes = $root.find( '.mw-htmlform-field-autoinfuse' );
                if ( $oouiNodes.length ) {
                        // The modules are preloaded (added server-side in HTMLFormField, and the individual fields
                        // which need extra ones), but this module doesn't depend on them. Wait until they're loaded.
                        modules = [ 'mediawiki.htmlform.ooui' ];
-                       if ( $oouiNodes.filter( '.mw-htmlform-field-HTMLTitleTextField' ).length ) {
-                               // FIXME: TitleInputWidget should be in its own module
-                               modules.push( 'mediawiki.widgets' );
-                       }
-                       if ( $oouiNodes.filter( '.mw-htmlform-field-HTMLUserTextField' ).length ) {
-                               modules.push( 'mediawiki.widgets.UserInputWidget' );
-                       }
-                       if (
-                               $oouiNodes.filter( '.mw-htmlform-field-HTMLSelectNamespace' ).length ||
-                               $oouiNodes.filter( '.mw-htmlform-field-HTMLSelectNamespaceWithButton' ).length
-                       ) {
-                               // FIXME: NamespaceInputWidget should be in its own module (probably?)
-                               modules.push( 'mediawiki.widgets' );
-                       }
+                       $oouiNodes.each( function () {
+                               var data = $( this ).data( 'mw-modules' );
+                               if ( data ) {
+                                       // We can trust this value, 'data-mw-*' attributes are banned from user content in Sanitizer
+                                       extraModules = data.split( ',' );
+                                       modules.push.apply( modules, extraModules );
+                               }
+                       } );
                        mw.loader.using( modules ).done( function () {
                                $oouiNodes.each( function () {
                                        OO.ui.infuse( this );
@@ -35,4 +29,4 @@
 
        } );
 
-}( mediaWiki ) );
+}( mediaWiki, jQuery ) );
index 6460ed1..cb717af 100644 (file)
 
        mw.hook( 'htmlform.enhance' ).add( function ( $root ) {
                $root.find( '.mw-htmlform-hide-if' ).each( function () {
-                       var v, i, fields, test, func, spec, self, modules,
+                       var v, i, fields, test, func, spec, self, modules, data,extraModules,
                                $el = $( this );
 
                        modules = [];
                        if ( $el.is( '[data-ooui]' ) ) {
                                modules.push( 'mediawiki.htmlform.ooui' );
-                               if ( $el.filter( '.mw-htmlform-field-HTMLTitleTextField' ).length ) {
-                                       // FIXME: TitleInputWidget should be in its own module
-                                       modules.push( 'mediawiki.widgets' );
-                               }
-                               if ( $el.filter( '.mw-htmlform-field-HTMLUserTextField' ).length ) {
-                                       modules.push( 'mediawiki.widgets.UserInputWidget' );
-                               }
-                               if (
-                                       $el.filter( '.mw-htmlform-field-HTMLSelectNamespace' ).length ||
-                                       $el.filter( '.mw-htmlform-field-HTMLSelectNamespaceWithButton' ).length
-                               ) {
-                                       // FIXME: NamespaceInputWidget should be in its own module (probably?)
-                                       modules.push( 'mediawiki.widgets' );
+                               data = $el.data( 'mw-modules' );
+                               if ( data ) {
+                                       // We can trust this value, 'data-mw-*' attributes are banned from user content in Sanitizer
+                                       extraModules = data.split( ',' );
+                                       modules.push.apply( modules, extraModules );
                                }
                        }