mediawiki.htmlform: Move files to their own module directory
authorTimo Tijhof <krinklemail@gmail.com>
Wed, 9 May 2018 19:10:05 +0000 (20:10 +0100)
committerKrinkle <krinklemail@gmail.com>
Wed, 9 May 2018 20:51:21 +0000 (20:51 +0000)
* mediawiki.htmlform.styles:
  - mediawiki/htmlform/styles.css
  - mediawiki/htmlform/images/*
    Only contains two images, only used by this module.

* mediawiki.htmlform.checker.js.

* mediawiki.htmlform.ooui: Only Element.js.

* mediawiki.htmlform.ooui.styles.less.

* mediawiki.htmlform: Other files from mediawiki/htmlform.

Bug: T193826
Change-Id: I5c057c758933e905d5c7940ade5bf43282088159

31 files changed:
resources/Resources.php
resources/src/mediawiki.htmlform.checker.js [new file with mode: 0644]
resources/src/mediawiki.htmlform.ooui.styles.less [new file with mode: 0644]
resources/src/mediawiki.htmlform.ooui/Element.js [new file with mode: 0644]
resources/src/mediawiki.htmlform.styles/images/question.png [new file with mode: 0644]
resources/src/mediawiki.htmlform.styles/images/question.svg [new file with mode: 0644]
resources/src/mediawiki.htmlform.styles/styles.css [new file with mode: 0644]
resources/src/mediawiki.htmlform/autocomplete.js [new file with mode: 0644]
resources/src/mediawiki.htmlform/autoinfuse.js [new file with mode: 0644]
resources/src/mediawiki.htmlform/checkmatrix.js [new file with mode: 0644]
resources/src/mediawiki.htmlform/cloner.js [new file with mode: 0644]
resources/src/mediawiki.htmlform/hide-if.js [new file with mode: 0644]
resources/src/mediawiki.htmlform/htmlform.js [new file with mode: 0644]
resources/src/mediawiki.htmlform/multiselect.js [new file with mode: 0644]
resources/src/mediawiki.htmlform/selectandother.js [new file with mode: 0644]
resources/src/mediawiki.htmlform/selectorother.js [new file with mode: 0644]
resources/src/mediawiki/htmlform/autocomplete.js [deleted file]
resources/src/mediawiki/htmlform/autoinfuse.js [deleted file]
resources/src/mediawiki/htmlform/checkmatrix.js [deleted file]
resources/src/mediawiki/htmlform/cloner.js [deleted file]
resources/src/mediawiki/htmlform/hide-if.js [deleted file]
resources/src/mediawiki/htmlform/htmlform.Checker.js [deleted file]
resources/src/mediawiki/htmlform/htmlform.Element.js [deleted file]
resources/src/mediawiki/htmlform/htmlform.js [deleted file]
resources/src/mediawiki/htmlform/images/question.png [deleted file]
resources/src/mediawiki/htmlform/images/question.svg [deleted file]
resources/src/mediawiki/htmlform/multiselect.js [deleted file]
resources/src/mediawiki/htmlform/ooui.styles.less [deleted file]
resources/src/mediawiki/htmlform/selectandother.js [deleted file]
resources/src/mediawiki/htmlform/selectorother.js [deleted file]
resources/src/mediawiki/htmlform/styles.css [deleted file]

index 1a3d2f0..81a32c2 100644 (file)
@@ -1073,15 +1073,15 @@ return [
        ],
        'mediawiki.htmlform' => [
                'scripts' => [
-                       'resources/src/mediawiki/htmlform/htmlform.js',
-                       'resources/src/mediawiki/htmlform/autocomplete.js',
-                       'resources/src/mediawiki/htmlform/autoinfuse.js',
-                       'resources/src/mediawiki/htmlform/checkmatrix.js',
-                       'resources/src/mediawiki/htmlform/cloner.js',
-                       'resources/src/mediawiki/htmlform/hide-if.js',
-                       'resources/src/mediawiki/htmlform/multiselect.js',
-                       'resources/src/mediawiki/htmlform/selectandother.js',
-                       'resources/src/mediawiki/htmlform/selectorother.js',
+                       'resources/src/mediawiki.htmlform/htmlform.js',
+                       'resources/src/mediawiki.htmlform/autocomplete.js',
+                       'resources/src/mediawiki.htmlform/autoinfuse.js',
+                       'resources/src/mediawiki.htmlform/checkmatrix.js',
+                       'resources/src/mediawiki.htmlform/cloner.js',
+                       'resources/src/mediawiki.htmlform/hide-if.js',
+                       'resources/src/mediawiki.htmlform/multiselect.js',
+                       'resources/src/mediawiki.htmlform/selectandother.js',
+                       'resources/src/mediawiki.htmlform/selectorother.js',
                ],
                'dependencies' => [
                        'mediawiki.RegExp',
@@ -1096,7 +1096,7 @@ return [
        ],
        'mediawiki.htmlform.checker' => [
                'scripts' => [
-                       'resources/src/mediawiki/htmlform/htmlform.Checker.js',
+                       'resources/src/mediawiki.htmlform.checker.js',
                ],
                'dependencies' => [
                        'jquery.throttle-debounce',
@@ -1105,7 +1105,7 @@ return [
        ],
        'mediawiki.htmlform.ooui' => [
                'scripts' => [
-                       'resources/src/mediawiki/htmlform/htmlform.Element.js',
+                       'resources/src/mediawiki.htmlform.ooui/Element.js',
                ],
                'dependencies' => [
                        'oojs-ui-core',
@@ -1113,11 +1113,11 @@ return [
                'targets' => [ 'desktop', 'mobile' ],
        ],
        'mediawiki.htmlform.styles' => [
-               'styles' => 'resources/src/mediawiki/htmlform/styles.css',
+               'styles' => 'resources/src/mediawiki.htmlform.styles/styles.css',
                'targets' => [ 'desktop', 'mobile' ],
        ],
        'mediawiki.htmlform.ooui.styles' => [
-               'styles' => 'resources/src/mediawiki/htmlform/ooui.styles.less',
+               'styles' => 'resources/src/mediawiki.htmlform.ooui.styles.less',
                'targets' => [ 'desktop', 'mobile' ],
        ],
        'mediawiki.icon' => [
diff --git a/resources/src/mediawiki.htmlform.checker.js b/resources/src/mediawiki.htmlform.checker.js
new file mode 100644 (file)
index 0000000..a9f08db
--- /dev/null
@@ -0,0 +1,181 @@
+( function ( mw, $ ) {
+
+       // FIXME: mw.htmlform.Element also sets this to empty object
+       mw.htmlform = {};
+
+       /**
+        * @class mw.htmlform.Checker
+        */
+
+       /**
+        * A helper class to add validation to non-OOUI HtmlForm fields.
+        *
+        * @constructor
+        * @param {jQuery} $element Form field generated by HTMLForm
+        * @param {Function} validator Validation callback
+        * @param {string} validator.value Value of the form field to be validated
+        * @param {jQuery.Promise} validator.return The promise should be resolved
+        *  with an object with two properties: Boolean 'valid' to indicate success
+        *  or failure of validation, and an array 'messages' to be passed to
+        *  setErrors() on failure.
+        */
+       mw.htmlform.Checker = function ( $element, validator ) {
+               this.validator = validator;
+               this.$element = $element;
+
+               this.$errorBox = $element.next( '.error' );
+               if ( !this.$errorBox.length ) {
+                       this.$errorBox = $( '<span>' );
+                       this.$errorBox.hide();
+                       $element.after( this.$errorBox );
+               }
+
+               this.currentValue = this.$element.val();
+       };
+
+       /**
+        * Attach validation events to the form element
+        *
+        * @param {jQuery} [$extraElements] Additional elements to listen for change
+        *  events on.
+        * @return {mw.htmlform.Checker}
+        * @chainable
+        */
+       mw.htmlform.Checker.prototype.attach = function ( $extraElements ) {
+               var $e,
+                       // We need to hook to all of these events to be sure we are
+                       // notified of all changes to the value of an <input type=text>
+                       // field.
+                       events = 'keyup keydown change mouseup cut paste focus blur';
+
+               $e = this.$element;
+               if ( $extraElements ) {
+                       $e = $e.add( $extraElements );
+               }
+               $e.on( events, $.debounce( 1000, this.validate.bind( this ) ) );
+
+               return this;
+       };
+
+       /**
+        * Validate the form element
+        * @return {jQuery.Promise}
+        */
+       mw.htmlform.Checker.prototype.validate = function () {
+               var currentRequestInternal,
+                       that = this,
+                       value = this.$element.val();
+
+               // Abort any pending requests.
+               if ( this.currentRequest && this.currentRequest.abort ) {
+                       this.currentRequest.abort();
+               }
+
+               if ( value === '' ) {
+                       this.currentValue = value;
+                       this.setErrors( [] );
+                       return;
+               }
+
+               this.currentRequest = currentRequestInternal = this.validator( value )
+                       .done( function ( info ) {
+                               var forceReplacement = value !== that.currentValue;
+
+                               // Another request was fired in the meantime, the result we got here is no longer current.
+                               // This shouldn't happen as we abort pending requests, but you never know.
+                               if ( that.currentRequest !== currentRequestInternal ) {
+                                       return;
+                               }
+                               // If we're here, then the current request has finished, avoid calling .abort() needlessly.
+                               that.currentRequest = undefined;
+
+                               that.currentValue = value;
+
+                               if ( info.valid ) {
+                                       that.setErrors( [], forceReplacement );
+                               } else {
+                                       that.setErrors( info.messages, forceReplacement );
+                               }
+                       } ).fail( function () {
+                               that.currentValue = null;
+                               that.setErrors( [] );
+                       } );
+
+               return currentRequestInternal;
+       };
+
+       /**
+        * Display errors associated with the form element
+        * @param {Array} errors Error messages. Each error message will be appended to a
+        *  `<span>` or `<li>`, as with jQuery.append().
+        * @param {boolean} [forceReplacement] Set true to force a visual replacement even
+        *  if the errors are the same. Ignored if errors are empty.
+        * @return {mw.htmlform.Checker}
+        * @chainable
+        */
+       mw.htmlform.Checker.prototype.setErrors = function ( errors, forceReplacement ) {
+               var $oldErrorBox, tagName, showFunc, text, replace,
+                       $errorBox = this.$errorBox;
+
+               if ( errors.length === 0 ) {
+                       $errorBox.slideUp( function () {
+                               $errorBox
+                                       .removeAttr( 'class' )
+                                       .empty();
+                       } );
+               } else {
+                       // Match behavior of HTMLFormField::formatErrors(), <span> or <ul>
+                       // depending on the count.
+                       tagName = errors.length === 1 ? 'span' : 'ul';
+
+                       // We have to animate the replacement if we're changing the tag. We
+                       // also want to if told to by the caller (i.e. to make it visually
+                       // obvious that the changed field value gives the same error) or if
+                       // the error text changes (because it makes more sense than
+                       // changing the text with no animation).
+                       replace = (
+                               forceReplacement || $errorBox.length > 1 ||
+                               $errorBox[ 0 ].tagName.toLowerCase() !== tagName
+                       );
+                       if ( !replace ) {
+                               text = $( '<' + tagName + '>' )
+                                       .append( errors.map( function ( e ) {
+                                               return errors.length === 1 ? e : $( '<li>' ).append( e );
+                                       } ) );
+                               if ( text.text() !== $errorBox.text() ) {
+                                       replace = true;
+                               }
+                       }
+
+                       $oldErrorBox = $errorBox;
+                       if ( replace ) {
+                               this.$errorBox = $errorBox = $( '<' + tagName + '>' );
+                               $errorBox.hide();
+                               $oldErrorBox.after( this.$errorBox );
+                       }
+
+                       showFunc = function () {
+                               if ( $oldErrorBox !== $errorBox ) {
+                                       $oldErrorBox
+                                               .removeAttr( 'class' )
+                                               .detach();
+                               }
+                               $errorBox
+                                       .attr( 'class', 'error' )
+                                       .empty()
+                                       .append( errors.map( function ( e ) {
+                                               return errors.length === 1 ? e : $( '<li>' ).append( e );
+                                       } ) )
+                                       .slideDown();
+                       };
+                       if ( $oldErrorBox !== $errorBox && $oldErrorBox.hasClass( 'error' ) ) {
+                               $oldErrorBox.slideUp( showFunc );
+                       } else {
+                               showFunc();
+                       }
+               }
+
+               return this;
+       };
+
+}( mediaWiki, jQuery ) );
diff --git a/resources/src/mediawiki.htmlform.ooui.styles.less b/resources/src/mediawiki.htmlform.ooui.styles.less
new file mode 100644 (file)
index 0000000..61a1c9c
--- /dev/null
@@ -0,0 +1,64 @@
+@import 'mediawiki.mixins';
+
+// OOUIHTMLForm styles
+@ooui-font-size-browser: 16; // assumed browser default of `16px`
+@ooui-font-size-base: 0.875em; // equals `14px` at browser default of `16px`
+
+@ooui-spacing-medium: 12 / @ooui-font-size-browser / @ooui-font-size-base; // equals `0.8571429em`≈`12px`
+@ooui-spacing-large: 16 / @ooui-font-size-browser / @ooui-font-size-base; // equals `1.1428571em`≈`16px`
+
+.mw-htmlform-ooui-wrapper.oo-ui-panelLayout-padded {
+       padding: @ooui-spacing-medium @ooui-spacing-large @ooui-spacing-large;
+}
+
+.mw-htmlform-ooui {
+       line-height: 1.4; // Override MediaWiki's default of `1.6`
+
+       .oo-ui-fieldLayout.oo-ui-labelElement > .oo-ui-fieldLayout-body > .oo-ui-fieldLayout-header {
+               line-height: 16 / @ooui-font-size-browser / @ooui-font-size-base;
+       }
+
+       .mw-htmlform-field-HTMLCheckMatrix {
+               width: 100%;
+       }
+
+       .mw-htmlform-matrix {
+               border-spacing: 0;
+
+               td {
+                       padding: 0.35em 0.7em;
+                       .transition( background-color 250ms );
+               }
+
+               tbody tr:nth-child( even ) td {
+                       background-color: #f8f9fa;
+               }
+
+               tbody tr:not( :first-child ):hover td {
+                       background-color: #eaecf0;
+               }
+
+               tbody tr:first-child td {
+                       background-color: #fff;
+               }
+
+               td.first {
+                       margin-right: 5%;
+                       width: 39%;
+               }
+       }
+}
+
+// Flatlist styling for PHP widgets...
+.mw-htmlform-flatlist .oo-ui-fieldLayout-align-inline,
+// ...and for JS widgets
+.mw-htmlform-flatlist .oo-ui-radioOptionWidget,
+.mw-htmlform-flatlist .oo-ui-checkboxMultioptionWidget {
+       display: inline-block;
+       margin-right: @ooui-spacing-medium;
+}
+
+.mw-htmlform-ooui .htmlform-tip,
+.mw-htmlform-ooui .mw-htmlform-submit-buttons {
+       margin-top: @ooui-spacing-medium;
+}
diff --git a/resources/src/mediawiki.htmlform.ooui/Element.js b/resources/src/mediawiki.htmlform.ooui/Element.js
new file mode 100644 (file)
index 0000000..e195ccb
--- /dev/null
@@ -0,0 +1,47 @@
+( function ( mw ) {
+
+       // FIXME: mw.htmlform.Checker also sets this to empty object
+       mw.htmlform = {};
+
+       /**
+        * Allows custom data specific to HTMLFormField to be set for OOUI forms. This picks up the
+        * extra config from a matching PHP widget (defined in HTMLFormElement.php) when constructed using
+        * OO.ui.infuse().
+        *
+        * Currently only supports passing 'hide-if' data.
+        *
+        * @ignore
+        * @param {Object} [config] Configuration options
+        */
+       mw.htmlform.Element = function ( config ) {
+               // Configuration initialization
+               config = config || {};
+
+               // Properties
+               this.hideIf = config.hideIf;
+
+               // Initialization
+               if ( this.hideIf ) {
+                       this.$element.addClass( 'mw-htmlform-hide-if' );
+               }
+       };
+
+       mw.htmlform.FieldLayout = function ( config ) {
+               // Parent constructor
+               mw.htmlform.FieldLayout.parent.call( this, config );
+               // Mixin constructors
+               mw.htmlform.Element.call( this, config );
+       };
+       OO.inheritClass( mw.htmlform.FieldLayout, OO.ui.FieldLayout );
+       OO.mixinClass( mw.htmlform.FieldLayout, mw.htmlform.Element );
+
+       mw.htmlform.ActionFieldLayout = function ( config ) {
+               // Parent constructor
+               mw.htmlform.ActionFieldLayout.parent.call( this, config );
+               // Mixin constructors
+               mw.htmlform.Element.call( this, config );
+       };
+       OO.inheritClass( mw.htmlform.ActionFieldLayout, OO.ui.ActionFieldLayout );
+       OO.mixinClass( mw.htmlform.ActionFieldLayout, mw.htmlform.Element );
+
+}( mediaWiki ) );
diff --git a/resources/src/mediawiki.htmlform.styles/images/question.png b/resources/src/mediawiki.htmlform.styles/images/question.png
new file mode 100644 (file)
index 0000000..7fc08a0
Binary files /dev/null and b/resources/src/mediawiki.htmlform.styles/images/question.png differ
diff --git a/resources/src/mediawiki.htmlform.styles/images/question.svg b/resources/src/mediawiki.htmlform.styles/images/question.svg
new file mode 100644 (file)
index 0000000..655076f
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="21.059" height="21.06">
+       <path fill="#54595d" d="M10.529 0c-5.814 0-10.529 4.714-10.529 10.529s4.715 10.53 10.529 10.53c5.816 0 10.529-4.715 10.529-10.53s-4.712-10.529-10.529-10.529zm-.002 16.767c-.861 0-1.498-.688-1.498-1.516 0-.862.637-1.534 1.498-1.534.828 0 1.5.672 1.5 1.534 0 .827-.672 1.516-1.5 1.516zm2.137-6.512c-.723.568-1 .931-1 1.739v.5h-2.205v-.603c0-1.517.449-2.136 1.154-2.688.707-.552 1.139-.845 1.139-1.637 0-.672-.414-1.051-1.24-1.051-.707 0-1.328.189-1.982.638l-1.051-1.807c.861-.604 1.93-1.034 3.342-1.034 1.912 0 3.516 1.051 3.516 3.066-.001 1.43-.794 2.188-1.673 2.877z"/>
+</svg>
diff --git a/resources/src/mediawiki.htmlform.styles/styles.css b/resources/src/mediawiki.htmlform.styles/styles.css
new file mode 100644 (file)
index 0000000..0f331ee
--- /dev/null
@@ -0,0 +1,54 @@
+/* HTMLForm styles */
+
+.mw-htmlform {
+       clear: both;
+}
+
+table.mw-htmlform-nolabel td.mw-label {
+       display: none;
+}
+
+.mw-htmlform-invalid-input td.mw-input input {
+       border-color: #d33;
+}
+
+.mw-htmlform-flatlist div.mw-htmlform-flatlist-item {
+       display: inline;
+       margin-right: 1em;
+       white-space: nowrap;
+}
+
+/* HTMLCheckMatrix */
+
+.mw-htmlform-matrix td {
+       padding-left: 0.5em;
+       padding-right: 0.5em;
+}
+
+tr.mw-htmlform-vertical-label td.mw-label {
+       text-align: left !important; /* stylelint-disable-line declaration-no-important */
+}
+
+.mw-icon-question {
+       /* SVG support using a transparent gradient to guarantee cross-browser
+        * compatibility (browsers able to understand gradient syntax support also SVG).
+        * http://pauginer.tumblr.com/post/36614680636/invisible-gradient-technique */
+       background-image: url( images/question.png );
+       /* @embed */
+       background-image: linear-gradient( transparent, transparent ), url( images/question.svg );
+       background-repeat: no-repeat;
+       background-size: 13px 13px;
+       display: inline-block;
+       height: 13px;
+       width: 13px;
+       margin-left: 4px;
+}
+
+/* stylelint-disable indentation */
+.mw-icon-question:lang( ar ),
+.mw-icon-question:lang( fa ),
+.mw-icon-question:lang( ur ) {
+       -webkit-transform: scaleX( -1 );
+       -ms-transform: scaleX( -1 );
+       transform: scaleX( -1 );
+}
diff --git a/resources/src/mediawiki.htmlform/autocomplete.js b/resources/src/mediawiki.htmlform/autocomplete.js
new file mode 100644 (file)
index 0000000..8157975
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * HTMLForm enhancements:
+ * Set up autocomplete fields.
+ */
+( function ( mw, $ ) {
+
+       mw.hook( 'htmlform.enhance' ).add( function ( $root ) {
+               var $autocomplete = $root.find( '.mw-htmlform-autocomplete' );
+               if ( $autocomplete.length ) {
+                       mw.loader.using( 'jquery.suggestions', function () {
+                               $autocomplete.suggestions( {
+                                       fetch: function ( val ) {
+                                               var $el = $( this );
+                                               $el.suggestions( 'suggestions',
+                                                       $.grep( $el.data( 'autocomplete' ), function ( v ) {
+                                                               return v.indexOf( val ) === 0;
+                                                       } )
+                                               );
+                                       }
+                               } );
+                       } );
+               }
+       } );
+
+}( mediaWiki, jQuery ) );
diff --git a/resources/src/mediawiki.htmlform/autoinfuse.js b/resources/src/mediawiki.htmlform/autoinfuse.js
new file mode 100644 (file)
index 0000000..c39e43a
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * HTMLForm enhancements:
+ * Infuse some OOUI HTMLForm fields (those which benefit from always being infused).
+ */
+( function ( mw, $ ) {
+
+       mw.hook( 'htmlform.enhance' ).add( function ( $root ) {
+               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' ];
+                       $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 );
+                               } );
+                       } );
+               }
+
+       } );
+
+}( mediaWiki, jQuery ) );
diff --git a/resources/src/mediawiki.htmlform/checkmatrix.js b/resources/src/mediawiki.htmlform/checkmatrix.js
new file mode 100644 (file)
index 0000000..b825f12
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * HTMLForm enhancements:
+ * Show fancy tooltips for checkmatrix fields.
+ */
+( function ( mw ) {
+
+       mw.hook( 'htmlform.enhance' ).add( function ( $root ) {
+               var $matrixTooltips = $root.find( '.mw-htmlform-matrix .mw-htmlform-tooltip' );
+               if ( $matrixTooltips.length ) {
+                       mw.loader.using( 'jquery.tipsy', function () {
+                               $matrixTooltips.tipsy( { gravity: 's' } );
+                       } );
+               }
+       } );
+
+}( mediaWiki ) );
diff --git a/resources/src/mediawiki.htmlform/cloner.js b/resources/src/mediawiki.htmlform/cloner.js
new file mode 100644 (file)
index 0000000..ab81580
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * HTMLForm enhancements:
+ * Add/remove cloner clones without having to resubmit the form.
+ */
+( function ( mw, $ ) {
+
+       var cloneCounter = 0;
+
+       mw.hook( 'htmlform.enhance' ).add( function ( $root ) {
+               $root.find( '.mw-htmlform-cloner-delete-button' ).filter( ':input' ).click( function ( ev ) {
+                       ev.preventDefault();
+                       $( this ).closest( 'li.mw-htmlform-cloner-li' ).remove();
+               } );
+
+               $root.find( '.mw-htmlform-cloner-create-button' ).filter( ':input' ).click( function ( ev ) {
+                       var $ul, $li, html;
+
+                       ev.preventDefault();
+
+                       $ul = $( this ).prev( 'ul.mw-htmlform-cloner-ul' );
+
+                       html = $ul.data( 'template' ).replace(
+                               new RegExp( mw.RegExp.escape( $ul.data( 'uniqueId' ) ), 'g' ),
+                               'clone' + ( ++cloneCounter )
+                       );
+
+                       $li = $( '<li>' )
+                               .addClass( 'mw-htmlform-cloner-li' )
+                               .html( html )
+                               .appendTo( $ul );
+
+                       mw.hook( 'htmlform.enhance' ).fire( $li );
+               } );
+       } );
+
+}( mediaWiki, jQuery ) );
diff --git a/resources/src/mediawiki.htmlform/hide-if.js b/resources/src/mediawiki.htmlform/hide-if.js
new file mode 100644 (file)
index 0000000..6d3c9fd
--- /dev/null
@@ -0,0 +1,295 @@
+/*
+ * HTMLForm enhancements:
+ * Set up 'hide-if' behaviors for form fields that have them.
+ */
+( function ( mw, $ ) {
+
+       /**
+        * Helper function for hide-if to find the nearby form field.
+        *
+        * Find the closest match for the given name, "closest" being the minimum
+        * level of parents to go to find a form field matching the given name or
+        * ending in array keys matching the given name (e.g. "baz" matches
+        * "foo[bar][baz]").
+        *
+        * @ignore
+        * @private
+        * @param {jQuery} $el
+        * @param {string} name
+        * @return {jQuery|OO.ui.Widget|null}
+        */
+       function hideIfGetField( $el, name ) {
+               var $found, $p, $widget,
+                       suffix = name.replace( /^([^[]+)/, '[$1]' );
+
+               function nameFilter() {
+                       return this.name === name ||
+                               ( this.name === ( 'wp' + name ) ) ||
+                               this.name.slice( -suffix.length ) === suffix;
+               }
+
+               for ( $p = $el.parent(); $p.length > 0; $p = $p.parent() ) {
+                       $found = $p.find( '[name]' ).filter( nameFilter );
+                       if ( $found.length ) {
+                               $widget = $found.closest( '.oo-ui-widget[data-ooui]' );
+                               if ( $widget.length ) {
+                                       return OO.ui.Widget.static.infuse( $widget );
+                               }
+                               return $found;
+                       }
+               }
+               return null;
+       }
+
+       /**
+        * Helper function for hide-if to return a test function and list of
+        * dependent fields for a hide-if specification.
+        *
+        * @ignore
+        * @private
+        * @param {jQuery} $el
+        * @param {Array} spec
+        * @return {Array}
+        * @return {Array} return.0 Dependent fields, array of jQuery objects or OO.ui.Widgets
+        * @return {Function} return.1 Test function
+        */
+       function hideIfParse( $el, spec ) {
+               var op, i, l, v, field, $field, fields, func, funcs, getVal;
+
+               op = spec[ 0 ];
+               l = spec.length;
+               switch ( op ) {
+                       case 'AND':
+                       case 'OR':
+                       case 'NAND':
+                       case 'NOR':
+                               funcs = [];
+                               fields = [];
+                               for ( i = 1; i < l; i++ ) {
+                                       if ( !Array.isArray( spec[ i ] ) ) {
+                                               throw new Error( op + ' parameters must be arrays' );
+                                       }
+                                       v = hideIfParse( $el, spec[ i ] );
+                                       fields = fields.concat( v[ 0 ] );
+                                       funcs.push( v[ 1 ] );
+                               }
+
+                               l = funcs.length;
+                               switch ( op ) {
+                                       case 'AND':
+                                               func = function () {
+                                                       var i;
+                                                       for ( i = 0; i < l; i++ ) {
+                                                               if ( !funcs[ i ]() ) {
+                                                                       return false;
+                                                               }
+                                                       }
+                                                       return true;
+                                               };
+                                               break;
+
+                                       case 'OR':
+                                               func = function () {
+                                                       var i;
+                                                       for ( i = 0; i < l; i++ ) {
+                                                               if ( funcs[ i ]() ) {
+                                                                       return true;
+                                                               }
+                                                       }
+                                                       return false;
+                                               };
+                                               break;
+
+                                       case 'NAND':
+                                               func = function () {
+                                                       var i;
+                                                       for ( i = 0; i < l; i++ ) {
+                                                               if ( !funcs[ i ]() ) {
+                                                                       return true;
+                                                               }
+                                                       }
+                                                       return false;
+                                               };
+                                               break;
+
+                                       case 'NOR':
+                                               func = function () {
+                                                       var i;
+                                                       for ( i = 0; i < l; i++ ) {
+                                                               if ( funcs[ i ]() ) {
+                                                                       return false;
+                                                               }
+                                                       }
+                                                       return true;
+                                               };
+                                               break;
+                               }
+
+                               return [ fields, func ];
+
+                       case 'NOT':
+                               if ( l !== 2 ) {
+                                       throw new Error( 'NOT takes exactly one parameter' );
+                               }
+                               if ( !Array.isArray( spec[ 1 ] ) ) {
+                                       throw new Error( 'NOT parameters must be arrays' );
+                               }
+                               v = hideIfParse( $el, spec[ 1 ] );
+                               fields = v[ 0 ];
+                               func = v[ 1 ];
+                               return [ fields, function () {
+                                       return !func();
+                               } ];
+
+                       case '===':
+                       case '!==':
+                               if ( l !== 3 ) {
+                                       throw new Error( op + ' takes exactly two parameters' );
+                               }
+                               field = hideIfGetField( $el, spec[ 1 ] );
+                               if ( !field ) {
+                                       return [ [], function () {
+                                               return false;
+                                       } ];
+                               }
+                               v = spec[ 2 ];
+
+                               if ( !( field instanceof jQuery ) ) {
+                                       // field is a OO.ui.Widget
+                                       if ( field.supports( 'isSelected' ) ) {
+                                               getVal = function () {
+                                                       var selected = field.isSelected();
+                                                       return selected ? field.getValue() : '';
+                                               };
+                                       } else {
+                                               getVal = function () {
+                                                       return field.getValue();
+                                               };
+                                       }
+                               } else {
+                                       $field = $( field );
+                                       if ( $field.prop( 'type' ) === 'radio' || $field.prop( 'type' ) === 'checkbox' ) {
+                                               getVal = function () {
+                                                       var $selected = $field.filter( ':checked' );
+                                                       return $selected.length ? $selected.val() : '';
+                                               };
+                                       } else {
+                                               getVal = function () {
+                                                       return $field.val();
+                                               };
+                                       }
+                               }
+
+                               switch ( op ) {
+                                       case '===':
+                                               func = function () {
+                                                       return getVal() === v;
+                                               };
+                                               break;
+                                       case '!==':
+                                               func = function () {
+                                                       return getVal() !== v;
+                                               };
+                                               break;
+                               }
+
+                               return [ [ field ], func ];
+
+                       default:
+                               throw new Error( 'Unrecognized operation \'' + op + '\'' );
+               }
+       }
+
+       mw.hook( 'htmlform.enhance' ).add( function ( $root ) {
+               var
+                       $fields = $root.find( '.mw-htmlform-hide-if' ),
+                       $oouiFields = $fields.filter( '[data-ooui]' ),
+                       modules = [];
+
+               if ( $oouiFields.length ) {
+                       modules.push( 'mediawiki.htmlform.ooui' );
+                       $oouiFields.each( function () {
+                               var data, extraModules,
+                                       $el = $( this );
+
+                               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 );
+                               }
+                       } );
+               }
+
+               mw.loader.using( modules ).done( function () {
+                       $fields.each( function () {
+                               var v, i, fields, test, func, spec, self,
+                                       $el = $( this );
+
+                               if ( $el.is( '[data-ooui]' ) ) {
+                                       // self should be a FieldLayout that mixes in mw.htmlform.Element
+                                       self = OO.ui.FieldLayout.static.infuse( $el );
+                                       spec = self.hideIf;
+                                       // The original element has been replaced with infused one
+                                       $el = self.$element;
+                               } else {
+                                       self = $el;
+                                       spec = $el.data( 'hideIf' );
+                               }
+
+                               if ( !spec ) {
+                                       return;
+                               }
+
+                               v = hideIfParse( $el, spec );
+                               fields = v[ 0 ];
+                               test = v[ 1 ];
+                               // The .toggle() method works mostly the same for jQuery objects and OO.ui.Widget
+                               func = function () {
+                                       var shouldHide = test();
+                                       self.toggle( !shouldHide );
+
+                                       // It is impossible to submit a form with hidden fields failing validation, e.g. one that
+                                       // is required. However, validity is not checked for disabled fields, as these are not
+                                       // submitted with the form. So we should also disable fields when hiding them.
+                                       if ( self instanceof jQuery ) {
+                                               // This also finds elements inside any nested fields (in case of HTMLFormFieldCloner),
+                                               // which is problematic. But it works because:
+                                               // * HTMLFormFieldCloner::createFieldsForKey() copies 'hide-if' rules to nested fields
+                                               // * jQuery collections like $fields are in document order, so we register event
+                                               //   handlers for parents first
+                                               // * Event handlers are fired in the order they were registered, so even if the handler
+                                               //   for parent messed up the child, the handle for child will run next and fix it
+                                               self.find( 'input, textarea, select' ).each( function () {
+                                                       var $this = $( this );
+                                                       if ( shouldHide ) {
+                                                               if ( $this.data( 'was-disabled' ) === undefined ) {
+                                                                       $this.data( 'was-disabled', $this.prop( 'disabled' ) );
+                                                               }
+                                                               $this.prop( 'disabled', true );
+                                                       } else {
+                                                               $this.prop( 'disabled', $this.data( 'was-disabled' ) );
+                                                       }
+                                               } );
+                                       } else {
+                                               // self is a OO.ui.FieldLayout
+                                               if ( shouldHide ) {
+                                                       if ( self.wasDisabled === undefined ) {
+                                                               self.wasDisabled = self.fieldWidget.isDisabled();
+                                                       }
+                                                       self.fieldWidget.setDisabled( true );
+                                               } else if ( self.wasDisabled !== undefined ) {
+                                                       self.fieldWidget.setDisabled( self.wasDisabled );
+                                               }
+                                       }
+                               };
+                               for ( i = 0; i < fields.length; i++ ) {
+                                       // The .on() method works mostly the same for jQuery objects and OO.ui.Widget
+                                       fields[ i ].on( 'change', func );
+                               }
+                               func();
+                       } );
+               } );
+       } );
+
+}( mediaWiki, jQuery ) );
diff --git a/resources/src/mediawiki.htmlform/htmlform.js b/resources/src/mediawiki.htmlform/htmlform.js
new file mode 100644 (file)
index 0000000..bc835b5
--- /dev/null
@@ -0,0 +1,14 @@
+( function ( mw, $ ) {
+
+       $( function () {
+               mw.hook( 'htmlform.enhance' ).fire( $( document ) );
+       } );
+
+       mw.hook( 'htmlform.enhance' ).add( function ( $root ) {
+               // Turn HTML5 form validation back on, in cases where it was disabled server-side (see
+               // HTMLForm::needsJSForHtml5FormValidation()) because we need extra logic implemented in JS to
+               // validate correctly. Currently, this is only used for forms containing fields with 'hide-if'.
+               $root.find( '.mw-htmlform' ).removeAttr( 'novalidate' );
+       } );
+
+}( mediaWiki, jQuery ) );
diff --git a/resources/src/mediawiki.htmlform/multiselect.js b/resources/src/mediawiki.htmlform/multiselect.js
new file mode 100644 (file)
index 0000000..e483763
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ * HTMLForm enhancements:
+ * Convert multiselect fields from checkboxes to Chosen selector when requested.
+ */
+( function ( mw, $ ) {
+
+       function addMulti( $oldContainer, $container ) {
+               var name = $oldContainer.find( 'input:first-child' ).attr( 'name' ),
+                       oldClass = ( ' ' + $oldContainer.attr( 'class' ) + ' ' ).replace( /(mw-htmlform-field-HTMLMultiSelectField|mw-chosen|mw-htmlform-dropdown)/g, '' ),
+                       $select = $( '<select>' ),
+                       dataPlaceholder = mw.message( 'htmlform-chosen-placeholder' );
+               oldClass = oldClass.trim();
+               $select.attr( {
+                       name: name,
+                       multiple: 'multiple',
+                       'data-placeholder': dataPlaceholder.plain(),
+                       'class': 'htmlform-chzn-select mw-input ' + oldClass
+               } );
+               $oldContainer.find( 'input' ).each( function () {
+                       var $oldInput = $( this ),
+                               checked = $oldInput.prop( 'checked' ),
+                               $option = $( '<option>' );
+                       $option.prop( 'value', $oldInput.prop( 'value' ) );
+                       if ( checked ) {
+                               $option.prop( 'selected', true );
+                       }
+                       $option.text( $oldInput.prop( 'value' ) );
+                       $select.append( $option );
+               } );
+               $container.append( $select );
+       }
+
+       function convertCheckboxesToMulti( $oldContainer, type ) {
+               var $fieldLabel = $( '<td>' ),
+                       $td = $( '<td>' ),
+                       $fieldLabelText = $( '<label>' ),
+                       $container;
+               if ( type === 'tr' ) {
+                       addMulti( $oldContainer, $td );
+                       $container = $( '<tr>' );
+                       $container.append( $td );
+               } else if ( type === 'div' ) {
+                       $fieldLabel = $( '<div>' );
+                       $container = $( '<div>' );
+                       addMulti( $oldContainer, $container );
+               }
+               $fieldLabel.attr( 'class', 'mw-label' );
+               $fieldLabelText.text( $oldContainer.find( '.mw-label label' ).text() );
+               $fieldLabel.append( $fieldLabelText );
+               $container.prepend( $fieldLabel );
+               $oldContainer.replaceWith( $container );
+               return $container;
+       }
+
+       function convertCheckboxesWidgetToTags( fieldLayout ) {
+               var checkboxesWidget, checkboxesOptions, menuTagOptions, menuTagWidget;
+
+               checkboxesWidget = fieldLayout.fieldWidget;
+               checkboxesOptions = checkboxesWidget.checkboxMultiselectWidget.getItems();
+               menuTagOptions = checkboxesOptions.map( function ( option ) {
+                       return new OO.ui.MenuOptionWidget( {
+                               data: option.getData(),
+                               label: option.getLabel()
+                       } );
+               } );
+               menuTagWidget = new OO.ui.MenuTagMultiselectWidget( {
+                       $overlay: true,
+                       menu: {
+                               items: menuTagOptions
+                       }
+               } );
+               menuTagWidget.setValue( checkboxesWidget.getValue() );
+
+               // Data from CapsuleMultiselectWidget will not be submitted with the form, so keep the original
+               // CheckboxMultiselectInputWidget up-to-date.
+               menuTagWidget.on( 'change', function () {
+                       checkboxesWidget.setValue( menuTagWidget.getValue() );
+               } );
+
+               // Hide original widget and add new one in its place. This is a bit hacky, since the FieldLayout
+               // still thinks it's connected to the old widget.
+               checkboxesWidget.toggle( false );
+               checkboxesWidget.$element.after( menuTagWidget.$element );
+       }
+
+       mw.hook( 'htmlform.enhance' ).add( function ( $root ) {
+               var $dropdowns = $root.find( '.mw-htmlform-field-HTMLMultiSelectField.mw-htmlform-dropdown' );
+               if ( $dropdowns.length ) {
+                       $dropdowns.each( function () {
+                               var $el = $( this ),
+                                       data, modules, extraModules;
+                               if ( $el.is( '[data-ooui]' ) ) {
+                                       // Load 'oojs-ui-widgets' for CapsuleMultiselectWidget
+                                       modules = [ 'mediawiki.htmlform.ooui', 'oojs-ui-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 );
+                                       }
+                                       mw.loader.using( modules, function () {
+                                               convertCheckboxesWidgetToTags( OO.ui.FieldLayout.static.infuse( $el ) );
+                                       } );
+                               } else {
+                                       mw.loader.using( 'jquery.chosen', function () {
+                                               var type = $el.is( 'tr' ) ? 'tr' : 'div',
+                                                       $converted = convertCheckboxesToMulti( $el, type );
+                                               $converted.find( '.htmlform-chzn-select' ).chosen( { width: 'auto' } );
+                                       } );
+                               }
+                       } );
+               }
+       } );
+
+}( mediaWiki, jQuery ) );
diff --git a/resources/src/mediawiki.htmlform/selectandother.js b/resources/src/mediawiki.htmlform/selectandother.js
new file mode 100644 (file)
index 0000000..fda6742
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * HTMLForm enhancements:
+ * Add a dynamic max length to the reason field of SelectAndOther.
+ */
+( function ( mw, $ ) {
+
+       // cache the separator to avoid object creation on each keypress
+       var colonSeparator = mw.message( 'colon-separator' ).text();
+
+       mw.hook( 'htmlform.enhance' ).add( function ( $root ) {
+               // This checks the length together with the value from the select field
+               // When the reason list is changed and the bytelimit is longer than the allowed,
+               // nothing is done
+               $root
+                       .find( '.mw-htmlform-select-and-other-field' )
+                       .each( function () {
+                               var $reasonList, currentValReasonList, maxlengthUnit, lengthLimiter, widget,
+                                       $this = $( this ),
+                                       $widget = $this.closest( '.oo-ui-widget[data-ooui]' );
+
+                               if ( $widget ) {
+                                       mw.loader.using( 'mediawiki.widgets.SelectWithInputWidget', function () {
+                                               widget = OO.ui.Widget.static.infuse( $widget );
+                                               maxlengthUnit = widget.getData().maxlengthUnit;
+                                               lengthLimiter = maxlengthUnit === 'codepoints' ? 'codePointLimit' : 'byteLimit';
+                                               widget.textinput.$input[ lengthLimiter ]( function ( input ) {
+                                                       // Should be built the same as in HTMLSelectAndOtherField::loadDataFromRequest
+                                                       var comment = widget.dropdowninput.getValue();
+                                                       if ( comment === 'other' ) {
+                                                               comment = input;
+                                                       } else if ( input !== '' ) {
+                                                               // Entry from drop down menu + additional comment
+                                                               comment += colonSeparator + input;
+                                                       }
+                                                       return comment;
+                                               } );
+                                       } );
+                               } else {
+                                       // find the reason list
+                                       $reasonList = $root.find( '#' + $this.data( 'id-select' ) );
+                                       // cache the current selection to avoid expensive lookup
+                                       currentValReasonList = $reasonList.val();
+
+                                       $reasonList.change( function () {
+                                               currentValReasonList = $reasonList.val();
+                                       } );
+
+                                       // Select the function for the length limit
+                                       maxlengthUnit = $this.data( 'mw-maxlength-unit' );
+                                       lengthLimiter = maxlengthUnit === 'codepoints' ? 'codePointLimit' : 'byteLimit';
+                                       $this[ lengthLimiter ]( function ( input ) {
+                                               // Should be built the same as in HTMLSelectAndOtherField::loadDataFromRequest
+                                               var comment = currentValReasonList;
+                                               if ( comment === 'other' ) {
+                                                       comment = input;
+                                               } else if ( input !== '' ) {
+                                                       // Entry from drop down menu + additional comment
+                                                       comment += colonSeparator + input;
+                                               }
+                                               return comment;
+                                       } );
+                               }
+                       } );
+       } );
+
+}( mediaWiki, jQuery ) );
diff --git a/resources/src/mediawiki.htmlform/selectorother.js b/resources/src/mediawiki.htmlform/selectorother.js
new file mode 100644 (file)
index 0000000..b6899d9
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * HTMLForm enhancements:
+ * Animate the SelectOrOther fields, to only show the text field when 'other' is selected.
+ */
+( function ( mw, $ ) {
+
+       /**
+        * @class jQuery.plugin.htmlform
+        */
+
+       /**
+        * jQuery plugin to fade or snap to visible state.
+        *
+        * @param {boolean} [instantToggle=false]
+        * @return {jQuery}
+        * @chainable
+        */
+       $.fn.goIn = function ( instantToggle ) {
+               if ( instantToggle === true ) {
+                       return this.show();
+               }
+               return this.stop( true, true ).fadeIn();
+       };
+
+       /**
+        * jQuery plugin to fade or snap to hiding state.
+        *
+        * @param {boolean} [instantToggle=false]
+        * @return {jQuery}
+        * @chainable
+        */
+       $.fn.goOut = function ( instantToggle ) {
+               if ( instantToggle === true ) {
+                       return this.hide();
+               }
+               return this.stop( true, true ).fadeOut();
+       };
+
+       mw.hook( 'htmlform.enhance' ).add( function ( $root ) {
+               /**
+                * @ignore
+                * @param {boolean|jQuery.Event} instant
+                */
+               function handleSelectOrOther( instant ) {
+                       var $other = $root.find( '#' + $( this ).attr( 'id' ) + '-other' );
+                       $other = $other.add( $other.siblings( 'br' ) );
+                       if ( $( this ).val() === 'other' ) {
+                               $other.goIn( instant );
+                       } else {
+                               $other.goOut( instant );
+                       }
+               }
+
+               $root
+                       .on( 'change', '.mw-htmlform-select-or-other', handleSelectOrOther )
+                       .find( '.mw-htmlform-select-or-other' )
+                       .each( function () {
+                               handleSelectOrOther.call( this, true );
+                       } );
+       } );
+
+}( mediaWiki, jQuery ) );
diff --git a/resources/src/mediawiki/htmlform/autocomplete.js b/resources/src/mediawiki/htmlform/autocomplete.js
deleted file mode 100644 (file)
index 8157975..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * HTMLForm enhancements:
- * Set up autocomplete fields.
- */
-( function ( mw, $ ) {
-
-       mw.hook( 'htmlform.enhance' ).add( function ( $root ) {
-               var $autocomplete = $root.find( '.mw-htmlform-autocomplete' );
-               if ( $autocomplete.length ) {
-                       mw.loader.using( 'jquery.suggestions', function () {
-                               $autocomplete.suggestions( {
-                                       fetch: function ( val ) {
-                                               var $el = $( this );
-                                               $el.suggestions( 'suggestions',
-                                                       $.grep( $el.data( 'autocomplete' ), function ( v ) {
-                                                               return v.indexOf( val ) === 0;
-                                                       } )
-                                               );
-                                       }
-                               } );
-                       } );
-               }
-       } );
-
-}( mediaWiki, jQuery ) );
diff --git a/resources/src/mediawiki/htmlform/autoinfuse.js b/resources/src/mediawiki/htmlform/autoinfuse.js
deleted file mode 100644 (file)
index c39e43a..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * HTMLForm enhancements:
- * Infuse some OOUI HTMLForm fields (those which benefit from always being infused).
- */
-( function ( mw, $ ) {
-
-       mw.hook( 'htmlform.enhance' ).add( function ( $root ) {
-               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' ];
-                       $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 );
-                               } );
-                       } );
-               }
-
-       } );
-
-}( mediaWiki, jQuery ) );
diff --git a/resources/src/mediawiki/htmlform/checkmatrix.js b/resources/src/mediawiki/htmlform/checkmatrix.js
deleted file mode 100644 (file)
index b825f12..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-/*
- * HTMLForm enhancements:
- * Show fancy tooltips for checkmatrix fields.
- */
-( function ( mw ) {
-
-       mw.hook( 'htmlform.enhance' ).add( function ( $root ) {
-               var $matrixTooltips = $root.find( '.mw-htmlform-matrix .mw-htmlform-tooltip' );
-               if ( $matrixTooltips.length ) {
-                       mw.loader.using( 'jquery.tipsy', function () {
-                               $matrixTooltips.tipsy( { gravity: 's' } );
-                       } );
-               }
-       } );
-
-}( mediaWiki ) );
diff --git a/resources/src/mediawiki/htmlform/cloner.js b/resources/src/mediawiki/htmlform/cloner.js
deleted file mode 100644 (file)
index ab81580..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * HTMLForm enhancements:
- * Add/remove cloner clones without having to resubmit the form.
- */
-( function ( mw, $ ) {
-
-       var cloneCounter = 0;
-
-       mw.hook( 'htmlform.enhance' ).add( function ( $root ) {
-               $root.find( '.mw-htmlform-cloner-delete-button' ).filter( ':input' ).click( function ( ev ) {
-                       ev.preventDefault();
-                       $( this ).closest( 'li.mw-htmlform-cloner-li' ).remove();
-               } );
-
-               $root.find( '.mw-htmlform-cloner-create-button' ).filter( ':input' ).click( function ( ev ) {
-                       var $ul, $li, html;
-
-                       ev.preventDefault();
-
-                       $ul = $( this ).prev( 'ul.mw-htmlform-cloner-ul' );
-
-                       html = $ul.data( 'template' ).replace(
-                               new RegExp( mw.RegExp.escape( $ul.data( 'uniqueId' ) ), 'g' ),
-                               'clone' + ( ++cloneCounter )
-                       );
-
-                       $li = $( '<li>' )
-                               .addClass( 'mw-htmlform-cloner-li' )
-                               .html( html )
-                               .appendTo( $ul );
-
-                       mw.hook( 'htmlform.enhance' ).fire( $li );
-               } );
-       } );
-
-}( mediaWiki, jQuery ) );
diff --git a/resources/src/mediawiki/htmlform/hide-if.js b/resources/src/mediawiki/htmlform/hide-if.js
deleted file mode 100644 (file)
index 6d3c9fd..0000000
+++ /dev/null
@@ -1,295 +0,0 @@
-/*
- * HTMLForm enhancements:
- * Set up 'hide-if' behaviors for form fields that have them.
- */
-( function ( mw, $ ) {
-
-       /**
-        * Helper function for hide-if to find the nearby form field.
-        *
-        * Find the closest match for the given name, "closest" being the minimum
-        * level of parents to go to find a form field matching the given name or
-        * ending in array keys matching the given name (e.g. "baz" matches
-        * "foo[bar][baz]").
-        *
-        * @ignore
-        * @private
-        * @param {jQuery} $el
-        * @param {string} name
-        * @return {jQuery|OO.ui.Widget|null}
-        */
-       function hideIfGetField( $el, name ) {
-               var $found, $p, $widget,
-                       suffix = name.replace( /^([^[]+)/, '[$1]' );
-
-               function nameFilter() {
-                       return this.name === name ||
-                               ( this.name === ( 'wp' + name ) ) ||
-                               this.name.slice( -suffix.length ) === suffix;
-               }
-
-               for ( $p = $el.parent(); $p.length > 0; $p = $p.parent() ) {
-                       $found = $p.find( '[name]' ).filter( nameFilter );
-                       if ( $found.length ) {
-                               $widget = $found.closest( '.oo-ui-widget[data-ooui]' );
-                               if ( $widget.length ) {
-                                       return OO.ui.Widget.static.infuse( $widget );
-                               }
-                               return $found;
-                       }
-               }
-               return null;
-       }
-
-       /**
-        * Helper function for hide-if to return a test function and list of
-        * dependent fields for a hide-if specification.
-        *
-        * @ignore
-        * @private
-        * @param {jQuery} $el
-        * @param {Array} spec
-        * @return {Array}
-        * @return {Array} return.0 Dependent fields, array of jQuery objects or OO.ui.Widgets
-        * @return {Function} return.1 Test function
-        */
-       function hideIfParse( $el, spec ) {
-               var op, i, l, v, field, $field, fields, func, funcs, getVal;
-
-               op = spec[ 0 ];
-               l = spec.length;
-               switch ( op ) {
-                       case 'AND':
-                       case 'OR':
-                       case 'NAND':
-                       case 'NOR':
-                               funcs = [];
-                               fields = [];
-                               for ( i = 1; i < l; i++ ) {
-                                       if ( !Array.isArray( spec[ i ] ) ) {
-                                               throw new Error( op + ' parameters must be arrays' );
-                                       }
-                                       v = hideIfParse( $el, spec[ i ] );
-                                       fields = fields.concat( v[ 0 ] );
-                                       funcs.push( v[ 1 ] );
-                               }
-
-                               l = funcs.length;
-                               switch ( op ) {
-                                       case 'AND':
-                                               func = function () {
-                                                       var i;
-                                                       for ( i = 0; i < l; i++ ) {
-                                                               if ( !funcs[ i ]() ) {
-                                                                       return false;
-                                                               }
-                                                       }
-                                                       return true;
-                                               };
-                                               break;
-
-                                       case 'OR':
-                                               func = function () {
-                                                       var i;
-                                                       for ( i = 0; i < l; i++ ) {
-                                                               if ( funcs[ i ]() ) {
-                                                                       return true;
-                                                               }
-                                                       }
-                                                       return false;
-                                               };
-                                               break;
-
-                                       case 'NAND':
-                                               func = function () {
-                                                       var i;
-                                                       for ( i = 0; i < l; i++ ) {
-                                                               if ( !funcs[ i ]() ) {
-                                                                       return true;
-                                                               }
-                                                       }
-                                                       return false;
-                                               };
-                                               break;
-
-                                       case 'NOR':
-                                               func = function () {
-                                                       var i;
-                                                       for ( i = 0; i < l; i++ ) {
-                                                               if ( funcs[ i ]() ) {
-                                                                       return false;
-                                                               }
-                                                       }
-                                                       return true;
-                                               };
-                                               break;
-                               }
-
-                               return [ fields, func ];
-
-                       case 'NOT':
-                               if ( l !== 2 ) {
-                                       throw new Error( 'NOT takes exactly one parameter' );
-                               }
-                               if ( !Array.isArray( spec[ 1 ] ) ) {
-                                       throw new Error( 'NOT parameters must be arrays' );
-                               }
-                               v = hideIfParse( $el, spec[ 1 ] );
-                               fields = v[ 0 ];
-                               func = v[ 1 ];
-                               return [ fields, function () {
-                                       return !func();
-                               } ];
-
-                       case '===':
-                       case '!==':
-                               if ( l !== 3 ) {
-                                       throw new Error( op + ' takes exactly two parameters' );
-                               }
-                               field = hideIfGetField( $el, spec[ 1 ] );
-                               if ( !field ) {
-                                       return [ [], function () {
-                                               return false;
-                                       } ];
-                               }
-                               v = spec[ 2 ];
-
-                               if ( !( field instanceof jQuery ) ) {
-                                       // field is a OO.ui.Widget
-                                       if ( field.supports( 'isSelected' ) ) {
-                                               getVal = function () {
-                                                       var selected = field.isSelected();
-                                                       return selected ? field.getValue() : '';
-                                               };
-                                       } else {
-                                               getVal = function () {
-                                                       return field.getValue();
-                                               };
-                                       }
-                               } else {
-                                       $field = $( field );
-                                       if ( $field.prop( 'type' ) === 'radio' || $field.prop( 'type' ) === 'checkbox' ) {
-                                               getVal = function () {
-                                                       var $selected = $field.filter( ':checked' );
-                                                       return $selected.length ? $selected.val() : '';
-                                               };
-                                       } else {
-                                               getVal = function () {
-                                                       return $field.val();
-                                               };
-                                       }
-                               }
-
-                               switch ( op ) {
-                                       case '===':
-                                               func = function () {
-                                                       return getVal() === v;
-                                               };
-                                               break;
-                                       case '!==':
-                                               func = function () {
-                                                       return getVal() !== v;
-                                               };
-                                               break;
-                               }
-
-                               return [ [ field ], func ];
-
-                       default:
-                               throw new Error( 'Unrecognized operation \'' + op + '\'' );
-               }
-       }
-
-       mw.hook( 'htmlform.enhance' ).add( function ( $root ) {
-               var
-                       $fields = $root.find( '.mw-htmlform-hide-if' ),
-                       $oouiFields = $fields.filter( '[data-ooui]' ),
-                       modules = [];
-
-               if ( $oouiFields.length ) {
-                       modules.push( 'mediawiki.htmlform.ooui' );
-                       $oouiFields.each( function () {
-                               var data, extraModules,
-                                       $el = $( this );
-
-                               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 );
-                               }
-                       } );
-               }
-
-               mw.loader.using( modules ).done( function () {
-                       $fields.each( function () {
-                               var v, i, fields, test, func, spec, self,
-                                       $el = $( this );
-
-                               if ( $el.is( '[data-ooui]' ) ) {
-                                       // self should be a FieldLayout that mixes in mw.htmlform.Element
-                                       self = OO.ui.FieldLayout.static.infuse( $el );
-                                       spec = self.hideIf;
-                                       // The original element has been replaced with infused one
-                                       $el = self.$element;
-                               } else {
-                                       self = $el;
-                                       spec = $el.data( 'hideIf' );
-                               }
-
-                               if ( !spec ) {
-                                       return;
-                               }
-
-                               v = hideIfParse( $el, spec );
-                               fields = v[ 0 ];
-                               test = v[ 1 ];
-                               // The .toggle() method works mostly the same for jQuery objects and OO.ui.Widget
-                               func = function () {
-                                       var shouldHide = test();
-                                       self.toggle( !shouldHide );
-
-                                       // It is impossible to submit a form with hidden fields failing validation, e.g. one that
-                                       // is required. However, validity is not checked for disabled fields, as these are not
-                                       // submitted with the form. So we should also disable fields when hiding them.
-                                       if ( self instanceof jQuery ) {
-                                               // This also finds elements inside any nested fields (in case of HTMLFormFieldCloner),
-                                               // which is problematic. But it works because:
-                                               // * HTMLFormFieldCloner::createFieldsForKey() copies 'hide-if' rules to nested fields
-                                               // * jQuery collections like $fields are in document order, so we register event
-                                               //   handlers for parents first
-                                               // * Event handlers are fired in the order they were registered, so even if the handler
-                                               //   for parent messed up the child, the handle for child will run next and fix it
-                                               self.find( 'input, textarea, select' ).each( function () {
-                                                       var $this = $( this );
-                                                       if ( shouldHide ) {
-                                                               if ( $this.data( 'was-disabled' ) === undefined ) {
-                                                                       $this.data( 'was-disabled', $this.prop( 'disabled' ) );
-                                                               }
-                                                               $this.prop( 'disabled', true );
-                                                       } else {
-                                                               $this.prop( 'disabled', $this.data( 'was-disabled' ) );
-                                                       }
-                                               } );
-                                       } else {
-                                               // self is a OO.ui.FieldLayout
-                                               if ( shouldHide ) {
-                                                       if ( self.wasDisabled === undefined ) {
-                                                               self.wasDisabled = self.fieldWidget.isDisabled();
-                                                       }
-                                                       self.fieldWidget.setDisabled( true );
-                                               } else if ( self.wasDisabled !== undefined ) {
-                                                       self.fieldWidget.setDisabled( self.wasDisabled );
-                                               }
-                                       }
-                               };
-                               for ( i = 0; i < fields.length; i++ ) {
-                                       // The .on() method works mostly the same for jQuery objects and OO.ui.Widget
-                                       fields[ i ].on( 'change', func );
-                               }
-                               func();
-                       } );
-               } );
-       } );
-
-}( mediaWiki, jQuery ) );
diff --git a/resources/src/mediawiki/htmlform/htmlform.Checker.js b/resources/src/mediawiki/htmlform/htmlform.Checker.js
deleted file mode 100644 (file)
index 3f53b63..0000000
+++ /dev/null
@@ -1,180 +0,0 @@
-( function ( mw, $ ) {
-
-       mw.htmlform = {};
-
-       /**
-        * @class mw.htmlform.Checker
-        */
-
-       /**
-        * A helper class to add validation to non-OOUI HtmlForm fields.
-        *
-        * @constructor
-        * @param {jQuery} $element Form field generated by HTMLForm
-        * @param {Function} validator Validation callback
-        * @param {string} validator.value Value of the form field to be validated
-        * @param {jQuery.Promise} validator.return The promise should be resolved
-        *  with an object with two properties: Boolean 'valid' to indicate success
-        *  or failure of validation, and an array 'messages' to be passed to
-        *  setErrors() on failure.
-        */
-       mw.htmlform.Checker = function ( $element, validator ) {
-               this.validator = validator;
-               this.$element = $element;
-
-               this.$errorBox = $element.next( '.error' );
-               if ( !this.$errorBox.length ) {
-                       this.$errorBox = $( '<span>' );
-                       this.$errorBox.hide();
-                       $element.after( this.$errorBox );
-               }
-
-               this.currentValue = this.$element.val();
-       };
-
-       /**
-        * Attach validation events to the form element
-        *
-        * @param {jQuery} [$extraElements] Additional elements to listen for change
-        *  events on.
-        * @return {mw.htmlform.Checker}
-        * @chainable
-        */
-       mw.htmlform.Checker.prototype.attach = function ( $extraElements ) {
-               var $e,
-                       // We need to hook to all of these events to be sure we are
-                       // notified of all changes to the value of an <input type=text>
-                       // field.
-                       events = 'keyup keydown change mouseup cut paste focus blur';
-
-               $e = this.$element;
-               if ( $extraElements ) {
-                       $e = $e.add( $extraElements );
-               }
-               $e.on( events, $.debounce( 1000, this.validate.bind( this ) ) );
-
-               return this;
-       };
-
-       /**
-        * Validate the form element
-        * @return {jQuery.Promise}
-        */
-       mw.htmlform.Checker.prototype.validate = function () {
-               var currentRequestInternal,
-                       that = this,
-                       value = this.$element.val();
-
-               // Abort any pending requests.
-               if ( this.currentRequest && this.currentRequest.abort ) {
-                       this.currentRequest.abort();
-               }
-
-               if ( value === '' ) {
-                       this.currentValue = value;
-                       this.setErrors( [] );
-                       return;
-               }
-
-               this.currentRequest = currentRequestInternal = this.validator( value )
-                       .done( function ( info ) {
-                               var forceReplacement = value !== that.currentValue;
-
-                               // Another request was fired in the meantime, the result we got here is no longer current.
-                               // This shouldn't happen as we abort pending requests, but you never know.
-                               if ( that.currentRequest !== currentRequestInternal ) {
-                                       return;
-                               }
-                               // If we're here, then the current request has finished, avoid calling .abort() needlessly.
-                               that.currentRequest = undefined;
-
-                               that.currentValue = value;
-
-                               if ( info.valid ) {
-                                       that.setErrors( [], forceReplacement );
-                               } else {
-                                       that.setErrors( info.messages, forceReplacement );
-                               }
-                       } ).fail( function () {
-                               that.currentValue = null;
-                               that.setErrors( [] );
-                       } );
-
-               return currentRequestInternal;
-       };
-
-       /**
-        * Display errors associated with the form element
-        * @param {Array} errors Error messages. Each error message will be appended to a
-        *  `<span>` or `<li>`, as with jQuery.append().
-        * @param {boolean} [forceReplacement] Set true to force a visual replacement even
-        *  if the errors are the same. Ignored if errors are empty.
-        * @return {mw.htmlform.Checker}
-        * @chainable
-        */
-       mw.htmlform.Checker.prototype.setErrors = function ( errors, forceReplacement ) {
-               var $oldErrorBox, tagName, showFunc, text, replace,
-                       $errorBox = this.$errorBox;
-
-               if ( errors.length === 0 ) {
-                       $errorBox.slideUp( function () {
-                               $errorBox
-                                       .removeAttr( 'class' )
-                                       .empty();
-                       } );
-               } else {
-                       // Match behavior of HTMLFormField::formatErrors(), <span> or <ul>
-                       // depending on the count.
-                       tagName = errors.length === 1 ? 'span' : 'ul';
-
-                       // We have to animate the replacement if we're changing the tag. We
-                       // also want to if told to by the caller (i.e. to make it visually
-                       // obvious that the changed field value gives the same error) or if
-                       // the error text changes (because it makes more sense than
-                       // changing the text with no animation).
-                       replace = (
-                               forceReplacement || $errorBox.length > 1 ||
-                               $errorBox[ 0 ].tagName.toLowerCase() !== tagName
-                       );
-                       if ( !replace ) {
-                               text = $( '<' + tagName + '>' )
-                                       .append( errors.map( function ( e ) {
-                                               return errors.length === 1 ? e : $( '<li>' ).append( e );
-                                       } ) );
-                               if ( text.text() !== $errorBox.text() ) {
-                                       replace = true;
-                               }
-                       }
-
-                       $oldErrorBox = $errorBox;
-                       if ( replace ) {
-                               this.$errorBox = $errorBox = $( '<' + tagName + '>' );
-                               $errorBox.hide();
-                               $oldErrorBox.after( this.$errorBox );
-                       }
-
-                       showFunc = function () {
-                               if ( $oldErrorBox !== $errorBox ) {
-                                       $oldErrorBox
-                                               .removeAttr( 'class' )
-                                               .detach();
-                               }
-                               $errorBox
-                                       .attr( 'class', 'error' )
-                                       .empty()
-                                       .append( errors.map( function ( e ) {
-                                               return errors.length === 1 ? e : $( '<li>' ).append( e );
-                                       } ) )
-                                       .slideDown();
-                       };
-                       if ( $oldErrorBox !== $errorBox && $oldErrorBox.hasClass( 'error' ) ) {
-                               $oldErrorBox.slideUp( showFunc );
-                       } else {
-                               showFunc();
-                       }
-               }
-
-               return this;
-       };
-
-}( mediaWiki, jQuery ) );
diff --git a/resources/src/mediawiki/htmlform/htmlform.Element.js b/resources/src/mediawiki/htmlform/htmlform.Element.js
deleted file mode 100644 (file)
index 01108e6..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-( function ( mw ) {
-
-       mw.htmlform = {};
-
-       /**
-        * Allows custom data specific to HTMLFormField to be set for OOUI forms. This picks up the
-        * extra config from a matching PHP widget (defined in HTMLFormElement.php) when constructed using
-        * OO.ui.infuse().
-        *
-        * Currently only supports passing 'hide-if' data.
-        *
-        * @ignore
-        * @param {Object} [config] Configuration options
-        */
-       mw.htmlform.Element = function ( config ) {
-               // Configuration initialization
-               config = config || {};
-
-               // Properties
-               this.hideIf = config.hideIf;
-
-               // Initialization
-               if ( this.hideIf ) {
-                       this.$element.addClass( 'mw-htmlform-hide-if' );
-               }
-       };
-
-       mw.htmlform.FieldLayout = function ( config ) {
-               // Parent constructor
-               mw.htmlform.FieldLayout.parent.call( this, config );
-               // Mixin constructors
-               mw.htmlform.Element.call( this, config );
-       };
-       OO.inheritClass( mw.htmlform.FieldLayout, OO.ui.FieldLayout );
-       OO.mixinClass( mw.htmlform.FieldLayout, mw.htmlform.Element );
-
-       mw.htmlform.ActionFieldLayout = function ( config ) {
-               // Parent constructor
-               mw.htmlform.ActionFieldLayout.parent.call( this, config );
-               // Mixin constructors
-               mw.htmlform.Element.call( this, config );
-       };
-       OO.inheritClass( mw.htmlform.ActionFieldLayout, OO.ui.ActionFieldLayout );
-       OO.mixinClass( mw.htmlform.ActionFieldLayout, mw.htmlform.Element );
-
-}( mediaWiki ) );
diff --git a/resources/src/mediawiki/htmlform/htmlform.js b/resources/src/mediawiki/htmlform/htmlform.js
deleted file mode 100644 (file)
index bc835b5..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-( function ( mw, $ ) {
-
-       $( function () {
-               mw.hook( 'htmlform.enhance' ).fire( $( document ) );
-       } );
-
-       mw.hook( 'htmlform.enhance' ).add( function ( $root ) {
-               // Turn HTML5 form validation back on, in cases where it was disabled server-side (see
-               // HTMLForm::needsJSForHtml5FormValidation()) because we need extra logic implemented in JS to
-               // validate correctly. Currently, this is only used for forms containing fields with 'hide-if'.
-               $root.find( '.mw-htmlform' ).removeAttr( 'novalidate' );
-       } );
-
-}( mediaWiki, jQuery ) );
diff --git a/resources/src/mediawiki/htmlform/images/question.png b/resources/src/mediawiki/htmlform/images/question.png
deleted file mode 100644 (file)
index 7fc08a0..0000000
Binary files a/resources/src/mediawiki/htmlform/images/question.png and /dev/null differ
diff --git a/resources/src/mediawiki/htmlform/images/question.svg b/resources/src/mediawiki/htmlform/images/question.svg
deleted file mode 100644 (file)
index 655076f..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="21.059" height="21.06">
-       <path fill="#54595d" d="M10.529 0c-5.814 0-10.529 4.714-10.529 10.529s4.715 10.53 10.529 10.53c5.816 0 10.529-4.715 10.529-10.53s-4.712-10.529-10.529-10.529zm-.002 16.767c-.861 0-1.498-.688-1.498-1.516 0-.862.637-1.534 1.498-1.534.828 0 1.5.672 1.5 1.534 0 .827-.672 1.516-1.5 1.516zm2.137-6.512c-.723.568-1 .931-1 1.739v.5h-2.205v-.603c0-1.517.449-2.136 1.154-2.688.707-.552 1.139-.845 1.139-1.637 0-.672-.414-1.051-1.24-1.051-.707 0-1.328.189-1.982.638l-1.051-1.807c.861-.604 1.93-1.034 3.342-1.034 1.912 0 3.516 1.051 3.516 3.066-.001 1.43-.794 2.188-1.673 2.877z"/>
-</svg>
diff --git a/resources/src/mediawiki/htmlform/multiselect.js b/resources/src/mediawiki/htmlform/multiselect.js
deleted file mode 100644 (file)
index e483763..0000000
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * HTMLForm enhancements:
- * Convert multiselect fields from checkboxes to Chosen selector when requested.
- */
-( function ( mw, $ ) {
-
-       function addMulti( $oldContainer, $container ) {
-               var name = $oldContainer.find( 'input:first-child' ).attr( 'name' ),
-                       oldClass = ( ' ' + $oldContainer.attr( 'class' ) + ' ' ).replace( /(mw-htmlform-field-HTMLMultiSelectField|mw-chosen|mw-htmlform-dropdown)/g, '' ),
-                       $select = $( '<select>' ),
-                       dataPlaceholder = mw.message( 'htmlform-chosen-placeholder' );
-               oldClass = oldClass.trim();
-               $select.attr( {
-                       name: name,
-                       multiple: 'multiple',
-                       'data-placeholder': dataPlaceholder.plain(),
-                       'class': 'htmlform-chzn-select mw-input ' + oldClass
-               } );
-               $oldContainer.find( 'input' ).each( function () {
-                       var $oldInput = $( this ),
-                               checked = $oldInput.prop( 'checked' ),
-                               $option = $( '<option>' );
-                       $option.prop( 'value', $oldInput.prop( 'value' ) );
-                       if ( checked ) {
-                               $option.prop( 'selected', true );
-                       }
-                       $option.text( $oldInput.prop( 'value' ) );
-                       $select.append( $option );
-               } );
-               $container.append( $select );
-       }
-
-       function convertCheckboxesToMulti( $oldContainer, type ) {
-               var $fieldLabel = $( '<td>' ),
-                       $td = $( '<td>' ),
-                       $fieldLabelText = $( '<label>' ),
-                       $container;
-               if ( type === 'tr' ) {
-                       addMulti( $oldContainer, $td );
-                       $container = $( '<tr>' );
-                       $container.append( $td );
-               } else if ( type === 'div' ) {
-                       $fieldLabel = $( '<div>' );
-                       $container = $( '<div>' );
-                       addMulti( $oldContainer, $container );
-               }
-               $fieldLabel.attr( 'class', 'mw-label' );
-               $fieldLabelText.text( $oldContainer.find( '.mw-label label' ).text() );
-               $fieldLabel.append( $fieldLabelText );
-               $container.prepend( $fieldLabel );
-               $oldContainer.replaceWith( $container );
-               return $container;
-       }
-
-       function convertCheckboxesWidgetToTags( fieldLayout ) {
-               var checkboxesWidget, checkboxesOptions, menuTagOptions, menuTagWidget;
-
-               checkboxesWidget = fieldLayout.fieldWidget;
-               checkboxesOptions = checkboxesWidget.checkboxMultiselectWidget.getItems();
-               menuTagOptions = checkboxesOptions.map( function ( option ) {
-                       return new OO.ui.MenuOptionWidget( {
-                               data: option.getData(),
-                               label: option.getLabel()
-                       } );
-               } );
-               menuTagWidget = new OO.ui.MenuTagMultiselectWidget( {
-                       $overlay: true,
-                       menu: {
-                               items: menuTagOptions
-                       }
-               } );
-               menuTagWidget.setValue( checkboxesWidget.getValue() );
-
-               // Data from CapsuleMultiselectWidget will not be submitted with the form, so keep the original
-               // CheckboxMultiselectInputWidget up-to-date.
-               menuTagWidget.on( 'change', function () {
-                       checkboxesWidget.setValue( menuTagWidget.getValue() );
-               } );
-
-               // Hide original widget and add new one in its place. This is a bit hacky, since the FieldLayout
-               // still thinks it's connected to the old widget.
-               checkboxesWidget.toggle( false );
-               checkboxesWidget.$element.after( menuTagWidget.$element );
-       }
-
-       mw.hook( 'htmlform.enhance' ).add( function ( $root ) {
-               var $dropdowns = $root.find( '.mw-htmlform-field-HTMLMultiSelectField.mw-htmlform-dropdown' );
-               if ( $dropdowns.length ) {
-                       $dropdowns.each( function () {
-                               var $el = $( this ),
-                                       data, modules, extraModules;
-                               if ( $el.is( '[data-ooui]' ) ) {
-                                       // Load 'oojs-ui-widgets' for CapsuleMultiselectWidget
-                                       modules = [ 'mediawiki.htmlform.ooui', 'oojs-ui-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 );
-                                       }
-                                       mw.loader.using( modules, function () {
-                                               convertCheckboxesWidgetToTags( OO.ui.FieldLayout.static.infuse( $el ) );
-                                       } );
-                               } else {
-                                       mw.loader.using( 'jquery.chosen', function () {
-                                               var type = $el.is( 'tr' ) ? 'tr' : 'div',
-                                                       $converted = convertCheckboxesToMulti( $el, type );
-                                               $converted.find( '.htmlform-chzn-select' ).chosen( { width: 'auto' } );
-                                       } );
-                               }
-                       } );
-               }
-       } );
-
-}( mediaWiki, jQuery ) );
diff --git a/resources/src/mediawiki/htmlform/ooui.styles.less b/resources/src/mediawiki/htmlform/ooui.styles.less
deleted file mode 100644 (file)
index 61a1c9c..0000000
+++ /dev/null
@@ -1,64 +0,0 @@
-@import 'mediawiki.mixins';
-
-// OOUIHTMLForm styles
-@ooui-font-size-browser: 16; // assumed browser default of `16px`
-@ooui-font-size-base: 0.875em; // equals `14px` at browser default of `16px`
-
-@ooui-spacing-medium: 12 / @ooui-font-size-browser / @ooui-font-size-base; // equals `0.8571429em`≈`12px`
-@ooui-spacing-large: 16 / @ooui-font-size-browser / @ooui-font-size-base; // equals `1.1428571em`≈`16px`
-
-.mw-htmlform-ooui-wrapper.oo-ui-panelLayout-padded {
-       padding: @ooui-spacing-medium @ooui-spacing-large @ooui-spacing-large;
-}
-
-.mw-htmlform-ooui {
-       line-height: 1.4; // Override MediaWiki's default of `1.6`
-
-       .oo-ui-fieldLayout.oo-ui-labelElement > .oo-ui-fieldLayout-body > .oo-ui-fieldLayout-header {
-               line-height: 16 / @ooui-font-size-browser / @ooui-font-size-base;
-       }
-
-       .mw-htmlform-field-HTMLCheckMatrix {
-               width: 100%;
-       }
-
-       .mw-htmlform-matrix {
-               border-spacing: 0;
-
-               td {
-                       padding: 0.35em 0.7em;
-                       .transition( background-color 250ms );
-               }
-
-               tbody tr:nth-child( even ) td {
-                       background-color: #f8f9fa;
-               }
-
-               tbody tr:not( :first-child ):hover td {
-                       background-color: #eaecf0;
-               }
-
-               tbody tr:first-child td {
-                       background-color: #fff;
-               }
-
-               td.first {
-                       margin-right: 5%;
-                       width: 39%;
-               }
-       }
-}
-
-// Flatlist styling for PHP widgets...
-.mw-htmlform-flatlist .oo-ui-fieldLayout-align-inline,
-// ...and for JS widgets
-.mw-htmlform-flatlist .oo-ui-radioOptionWidget,
-.mw-htmlform-flatlist .oo-ui-checkboxMultioptionWidget {
-       display: inline-block;
-       margin-right: @ooui-spacing-medium;
-}
-
-.mw-htmlform-ooui .htmlform-tip,
-.mw-htmlform-ooui .mw-htmlform-submit-buttons {
-       margin-top: @ooui-spacing-medium;
-}
diff --git a/resources/src/mediawiki/htmlform/selectandother.js b/resources/src/mediawiki/htmlform/selectandother.js
deleted file mode 100644 (file)
index fda6742..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * HTMLForm enhancements:
- * Add a dynamic max length to the reason field of SelectAndOther.
- */
-( function ( mw, $ ) {
-
-       // cache the separator to avoid object creation on each keypress
-       var colonSeparator = mw.message( 'colon-separator' ).text();
-
-       mw.hook( 'htmlform.enhance' ).add( function ( $root ) {
-               // This checks the length together with the value from the select field
-               // When the reason list is changed and the bytelimit is longer than the allowed,
-               // nothing is done
-               $root
-                       .find( '.mw-htmlform-select-and-other-field' )
-                       .each( function () {
-                               var $reasonList, currentValReasonList, maxlengthUnit, lengthLimiter, widget,
-                                       $this = $( this ),
-                                       $widget = $this.closest( '.oo-ui-widget[data-ooui]' );
-
-                               if ( $widget ) {
-                                       mw.loader.using( 'mediawiki.widgets.SelectWithInputWidget', function () {
-                                               widget = OO.ui.Widget.static.infuse( $widget );
-                                               maxlengthUnit = widget.getData().maxlengthUnit;
-                                               lengthLimiter = maxlengthUnit === 'codepoints' ? 'codePointLimit' : 'byteLimit';
-                                               widget.textinput.$input[ lengthLimiter ]( function ( input ) {
-                                                       // Should be built the same as in HTMLSelectAndOtherField::loadDataFromRequest
-                                                       var comment = widget.dropdowninput.getValue();
-                                                       if ( comment === 'other' ) {
-                                                               comment = input;
-                                                       } else if ( input !== '' ) {
-                                                               // Entry from drop down menu + additional comment
-                                                               comment += colonSeparator + input;
-                                                       }
-                                                       return comment;
-                                               } );
-                                       } );
-                               } else {
-                                       // find the reason list
-                                       $reasonList = $root.find( '#' + $this.data( 'id-select' ) );
-                                       // cache the current selection to avoid expensive lookup
-                                       currentValReasonList = $reasonList.val();
-
-                                       $reasonList.change( function () {
-                                               currentValReasonList = $reasonList.val();
-                                       } );
-
-                                       // Select the function for the length limit
-                                       maxlengthUnit = $this.data( 'mw-maxlength-unit' );
-                                       lengthLimiter = maxlengthUnit === 'codepoints' ? 'codePointLimit' : 'byteLimit';
-                                       $this[ lengthLimiter ]( function ( input ) {
-                                               // Should be built the same as in HTMLSelectAndOtherField::loadDataFromRequest
-                                               var comment = currentValReasonList;
-                                               if ( comment === 'other' ) {
-                                                       comment = input;
-                                               } else if ( input !== '' ) {
-                                                       // Entry from drop down menu + additional comment
-                                                       comment += colonSeparator + input;
-                                               }
-                                               return comment;
-                                       } );
-                               }
-                       } );
-       } );
-
-}( mediaWiki, jQuery ) );
diff --git a/resources/src/mediawiki/htmlform/selectorother.js b/resources/src/mediawiki/htmlform/selectorother.js
deleted file mode 100644 (file)
index b6899d9..0000000
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * HTMLForm enhancements:
- * Animate the SelectOrOther fields, to only show the text field when 'other' is selected.
- */
-( function ( mw, $ ) {
-
-       /**
-        * @class jQuery.plugin.htmlform
-        */
-
-       /**
-        * jQuery plugin to fade or snap to visible state.
-        *
-        * @param {boolean} [instantToggle=false]
-        * @return {jQuery}
-        * @chainable
-        */
-       $.fn.goIn = function ( instantToggle ) {
-               if ( instantToggle === true ) {
-                       return this.show();
-               }
-               return this.stop( true, true ).fadeIn();
-       };
-
-       /**
-        * jQuery plugin to fade or snap to hiding state.
-        *
-        * @param {boolean} [instantToggle=false]
-        * @return {jQuery}
-        * @chainable
-        */
-       $.fn.goOut = function ( instantToggle ) {
-               if ( instantToggle === true ) {
-                       return this.hide();
-               }
-               return this.stop( true, true ).fadeOut();
-       };
-
-       mw.hook( 'htmlform.enhance' ).add( function ( $root ) {
-               /**
-                * @ignore
-                * @param {boolean|jQuery.Event} instant
-                */
-               function handleSelectOrOther( instant ) {
-                       var $other = $root.find( '#' + $( this ).attr( 'id' ) + '-other' );
-                       $other = $other.add( $other.siblings( 'br' ) );
-                       if ( $( this ).val() === 'other' ) {
-                               $other.goIn( instant );
-                       } else {
-                               $other.goOut( instant );
-                       }
-               }
-
-               $root
-                       .on( 'change', '.mw-htmlform-select-or-other', handleSelectOrOther )
-                       .find( '.mw-htmlform-select-or-other' )
-                       .each( function () {
-                               handleSelectOrOther.call( this, true );
-                       } );
-       } );
-
-}( mediaWiki, jQuery ) );
diff --git a/resources/src/mediawiki/htmlform/styles.css b/resources/src/mediawiki/htmlform/styles.css
deleted file mode 100644 (file)
index 0f331ee..0000000
+++ /dev/null
@@ -1,54 +0,0 @@
-/* HTMLForm styles */
-
-.mw-htmlform {
-       clear: both;
-}
-
-table.mw-htmlform-nolabel td.mw-label {
-       display: none;
-}
-
-.mw-htmlform-invalid-input td.mw-input input {
-       border-color: #d33;
-}
-
-.mw-htmlform-flatlist div.mw-htmlform-flatlist-item {
-       display: inline;
-       margin-right: 1em;
-       white-space: nowrap;
-}
-
-/* HTMLCheckMatrix */
-
-.mw-htmlform-matrix td {
-       padding-left: 0.5em;
-       padding-right: 0.5em;
-}
-
-tr.mw-htmlform-vertical-label td.mw-label {
-       text-align: left !important; /* stylelint-disable-line declaration-no-important */
-}
-
-.mw-icon-question {
-       /* SVG support using a transparent gradient to guarantee cross-browser
-        * compatibility (browsers able to understand gradient syntax support also SVG).
-        * http://pauginer.tumblr.com/post/36614680636/invisible-gradient-technique */
-       background-image: url( images/question.png );
-       /* @embed */
-       background-image: linear-gradient( transparent, transparent ), url( images/question.svg );
-       background-repeat: no-repeat;
-       background-size: 13px 13px;
-       display: inline-block;
-       height: 13px;
-       width: 13px;
-       margin-left: 4px;
-}
-
-/* stylelint-disable indentation */
-.mw-icon-question:lang( ar ),
-.mw-icon-question:lang( fa ),
-.mw-icon-question:lang( ur ) {
-       -webkit-transform: scaleX( -1 );
-       -ms-transform: scaleX( -1 );
-       transform: scaleX( -1 );
-}