Clean up mediawiki.legacy.protect a bit.
authorAlex Monk <krenair@wikimedia.org>
Fri, 29 Aug 2014 22:40:10 +0000 (23:40 +0100)
committerAlex Monk <krenair@wikimedia.org>
Sat, 13 Sep 2014 14:43:30 +0000 (15:43 +0100)
Also kills some inline JS stuff in ProtectionForm

Bug: 33871
Change-Id: Ie43c80bf5ebd6107458d5284cc68b7128f29359a

includes/ProtectionForm.php
resources/Resources.php
resources/src/mediawiki.legacy/protect.js

index 4aa65d9..35352d6 100644 (file)
@@ -352,7 +352,7 @@ class ProtectionForm {
                        $output->addJsConfigVars( 'wgCascadeableLevels', $cascadingRestrictionLevels );
                        $out .= Xml::openElement( 'form', array( 'method' => 'post',
                                'action' => $this->mTitle->getLocalURL( 'action=protect' ),
-                               'id' => 'mw-Protect-Form', 'onsubmit' => 'ProtectionForm.enableUnchainedInputs(true)' ) );
+                               'id' => 'mw-Protect-Form' ) );
                }
 
                $out .= Xml::openElement( 'fieldset' ) .
@@ -426,16 +426,13 @@ class ProtectionForm {
                                                                array(
                                                                        'id' => "mwProtectExpirySelection-$action",
                                                                        'name' => "wpProtectExpirySelection-$action",
-                                                                       'onchange' => "ProtectionForm.updateExpiryList(this)",
                                                                        'tabindex' => '2' ) + $this->disabledAttrib,
                                                                $expiryFormOptions ) .
                                                "</td>
                                        </tr></table>";
                        }
                        # Add custom expiry field
-                       $attribs = array( 'id' => "mwProtect-$action-expires",
-                               'onkeyup' => 'ProtectionForm.updateExpiry(this)',
-                               'onchange' => 'ProtectionForm.updateExpiry(this)' ) + $this->disabledAttrib;
+                       $attribs = array( 'id' => "mwProtect-$action-expires" ) + $this->disabledAttrib;
                        $out .= "<table><tr>
                                        <td class='mw-label'>" .
                                                $mProtectother .
@@ -556,7 +553,6 @@ class ProtectionForm {
                                $user->getEditToken( array( 'protect', $this->mTitle->getPrefixedDBkey() ) )
                        );
                        $out .= Xml::closeElement( 'form' );
-                       $output->addScript( $this->buildCleanupScript() );
                }
 
                return $out;
@@ -581,8 +577,7 @@ class ProtectionForm {
                        'id' => $id,
                        'name' => $id,
                        'size' => count( $levels ),
-                       'onchange' => 'ProtectionForm.updateLevels(this)',
-                       ) + $this->disabledAttrib;
+               ) + $this->disabledAttrib;
 
                $out = Xml::openElement( 'select', $attribs );
                foreach ( $levels as $key ) {
@@ -611,19 +606,6 @@ class ProtectionForm {
                }
        }
 
-       function buildCleanupScript() {
-               $options = array(
-                       'tableId' => 'mwProtectSet',
-                       'labelText' => wfMessage( 'protect-unchain-permissions' )->plain(),
-                       'numTypes' => count( $this->mApplicableTypes ),
-                       'existingMatch' => count( array_unique( $this->mExistingExpiry ) ) === 1,
-               );
-
-               $script = Xml::encodeJsCall( 'ProtectionForm.init', array( $options ) );
-
-               return Html::inlineScript( ResourceLoader::makeLoaderConditionalScript( $script ) );
-       }
-
        /**
         * Show protection long extracts for this page
         *
index 49f62b4..166dd7e 100644 (file)
@@ -1430,7 +1430,7 @@ return array(
                'dependencies' => array(
                        'jquery.byteLimit',
                ),
-               'position' => 'top',
+               'messages' => array( 'protect-unchain-permissions' )
        ),
        'mediawiki.legacy.shared' => array(
                // Used in the web installer. Test it after modifying this definition!
index dc142ca..f9069b6 100644 (file)
 ( function ( mw, $ ) {
 
 var ProtectionForm = window.ProtectionForm = {
-       existingMatch: false,
-
        /**
         * Set up the protection chaining interface (i.e. "unlock move permissions" checkbox)
         * on the protection form
-        *
-        * @param opts Object : parameters with members:
-        *     tableId              Identifier of the table containing UI bits
-        *     labelText            Text to use for the checkbox label
-        *     numTypes             The number of protection types
-        *     existingMatch        True if all the existing expiry times match
         */
-       init: function ( opts ) {
-               var box, boxbody, row, cell, check, label;
+       init: function () {
+               var $cell = $( '<td>' ), $row = $( '<tr>' ).append( $cell );
 
-               if ( !( document.createTextNode && document.getElementById && document.getElementsByTagName ) ) {
+               if ( !$( '#mwProtectSet' ).length ) {
                        return false;
                }
 
-               box = document.getElementById( opts.tableId );
-               if ( !box ) {
-                       return false;
+               if ( mw.config.get( 'wgCascadeableLevels' ) !== undefined ) {
+                       $( 'form#mw-Protect-Form' ).submit( this.toggleUnchainedInputs.bind( ProtectionForm, true ) );
                }
+               this.getExpirySelectors().each( function () {
+                       $( this ).change( ProtectionForm.updateExpiryList.bind( ProtectionForm, this ) );
+               } );
+               this.getExpiryInputs().each( function () {
+                       $( this ).on( 'keyup change', ProtectionForm.updateExpiry.bind( ProtectionForm, this ) );
+               } );
+               this.getLevelSelectors().each( function () {
+                       $( this ).change( ProtectionForm.updateLevels.bind( ProtectionForm, this ) );
+               } );
 
-               boxbody = box.getElementsByTagName( 'tbody' )[0];
-               row = document.createElement( 'tr' );
-               boxbody.insertBefore( row, boxbody.firstChild.nextSibling );
-
-               this.existingMatch = opts.existingMatch;
+               $( '#mwProtectSet > tbody > tr:first' ).after( $row );
 
-               cell = document.createElement( 'td' );
-               row.appendChild( cell );
                // If there is only one protection type, there is nothing to chain
-               if ( opts.numTypes > 1 ) {
-                       check = document.createElement( 'input' );
-                       check.id = 'mwProtectUnchained';
-                       check.type = 'checkbox';
-                       $( check ).click( function () {
-                               ProtectionForm.onChainClick();
-                       } );
-
-                       label = document.createElement( 'label' );
-                       label.htmlFor = 'mwProtectUnchained';
-                       label.appendChild( document.createTextNode( opts.labelText ) );
-
-                       cell.appendChild( check );
-                       cell.appendChild( document.createTextNode( ' ' ) );
-                       cell.appendChild( label );
+               if ( $( '[id ^= mw-protect-table-]' ).length > 1 ) {
+                       $cell.append(
+                               $( '<input>' )
+                                       .attr( { id: 'mwProtectUnchained', type: 'checkbox' } )
+                                       .click( this.onChainClick.bind( this ) )
+                                       .prop( 'checked', !this.areAllTypesMatching() ),
+                               document.createTextNode( ' ' ),
+                               $( '<label>' )
+                                       .attr( 'for', 'mwProtectUnchained' )
+                                       .text( mw.msg( 'protect-unchain-permissions' ) )
+                       );
 
-                       check.checked = !this.areAllTypesMatching();
-                       this.enableUnchainedInputs( check.checked );
+                       this.toggleUnchainedInputs( !this.areAllTypesMatching() );
                }
 
                $( '#mwProtect-reason' ).byteLimit( 180 );
 
                this.updateCascadeCheckbox();
-
-               return true;
        },
 
        /**
         * Sets the disabled attribute on the cascade checkbox depending on the current selected levels
         */
        updateCascadeCheckbox: function () {
-               var i, lists, items, selected;
-
-               // For non-existent titles, there is no cascade option
-               if ( !document.getElementById( 'mwProtect-cascade' ) ) {
-                       return;
-               }
-               lists = this.getLevelSelectors();
-               for ( i = 0; i < lists.length; i++ ) {
-                       if ( lists[i].selectedIndex > -1 ) {
-                               items = lists[i].getElementsByTagName( 'option' );
-                               selected = items[ lists[i].selectedIndex ].value;
-                               if ( !this.isCascadeableLevel( selected ) ) {
-                                       document.getElementById( 'mwProtect-cascade' ).checked = false;
-                                       document.getElementById( 'mwProtect-cascade' ).disabled = true;
-                                       return;
-                               }
+               this.getLevelSelectors().each( function () {
+                       if ( !ProtectionForm.isCascadeableLevel( $( this ).val() ) ) {
+                               $( '#mwProtect-cascade' ).prop( { checked: false, disabled: true } );
+                               return false;
+                       } else {
+                               $( '#mwProtect-cascade' ).prop( 'disabled', false );
                        }
-               }
-               document.getElementById( 'mwProtect-cascade' ).disabled = false;
+               } );
        },
 
        /**
         * Checks if a cerain protection level is cascadeable.
-        * @param level {String}
-        * @return {Boolean}
+        *
+        * @param {string} level
+        * @return {boolean}
         */
-       isCascadeableLevel: function (  level ) {
-               var cascadeLevels, len, i;
-
-               cascadeLevels = mw.config.get( 'wgCascadeableLevels' );
-               // cascadeLevels isn't defined on all pages
-               if ( cascadeLevels ) {
-                       for ( i = 0, len = cascadeLevels.length; i < len; i += 1 ) {
-                               if ( cascadeLevels[i] === level ) {
-                                       return true;
-                               }
-                       }
-               }
-               return false;
+       isCascadeableLevel: function ( level ) {
+               return $.inArray( level, mw.config.get( 'wgCascadeableLevels' ) ) !== -1;
        },
 
        /**
         * When protection levels are locked together, update the rest
         * when one action's level changes
         *
-        * @param source Element Level selector that changed
+        * @param {Element} source Level selector that changed
         */
        updateLevels: function ( source ) {
                if ( !this.isUnchained() ) {
@@ -123,28 +89,21 @@ var ProtectionForm = window.ProtectionForm = {
         * When protection levels are locked together, update the
         * expiries when one changes
         *
-        * @param source Element expiry input that changed
+        * @param {Element} source expiry input that changed
         */
 
        updateExpiry: function ( source ) {
-               var expiry, listId, list;
-
                if ( !this.isUnchained() ) {
-                       expiry = source.value;
-                       this.forEachExpiryInput( function ( element ) {
-                               element.value = expiry;
+                       this.getExpiryInputs().each( function () {
+                               this.value = source.value;
                        } );
                }
-               listId = source.id.replace( /^mwProtect-(\w+)-expires$/, 'mwProtectExpirySelection-$1' );
-               list = document.getElementById( listId );
-               if ( list && list.value !== 'othertime' ) {
-                       if ( this.isUnchained() ) {
-                               list.value = 'othertime';
-                       } else {
-                               this.forEachExpirySelector( function ( element ) {
-                                       element.value = 'othertime';
-                               } );
-                       }
+               if ( this.isUnchained() ) {
+                       $( '#' + source.id.replace( /^mwProtect-(\w+)-expires$/, 'mwProtectExpirySelection-$1' ) ).val( 'othertime' );
+               } else {
+                       this.getExpirySelectors().each( function () {
+                               this.value = 'othertime';
+                       } );
                }
        },
 
@@ -152,17 +111,15 @@ var ProtectionForm = window.ProtectionForm = {
         * When protection levels are locked together, update the
         * expiry lists when one changes and clear the custom inputs
         *
-        * @param source Element expiry selector that changed
+        * @param {Element} source Expiry selector that changed
         */
        updateExpiryList: function ( source ) {
-               var expiry;
                if ( !this.isUnchained() ) {
-                       expiry = source.value;
-                       this.forEachExpirySelector( function ( element ) {
-                               element.value = expiry;
+                       this.getExpirySelectors().each( function () {
+                               this.value = source.value;
                        } );
-                       this.forEachExpiryInput( function ( element ) {
-                               element.value = '';
+                       this.getExpiryInputs().each( function () {
+                               this.value = '';
                        } );
                }
        },
@@ -172,44 +129,35 @@ var ProtectionForm = window.ProtectionForm = {
         * when the user changes the "unlock move permissions" checkbox
         */
        onChainClick: function () {
-               if ( this.isUnchained() ) {
-                       this.enableUnchainedInputs( true );
-               } else {
+               this.toggleUnchainedInputs( this.isUnchained() );
+               if ( !this.isUnchained() ) {
                        this.setAllSelectors( this.getMaxLevel() );
-                       this.enableUnchainedInputs( false );
                }
                this.updateCascadeCheckbox();
        },
 
        /**
         * Returns true if the named attribute in all objects in the given array are matching
+        *
+        * @param {Object[]} objects
+        * @param {string} attrName
+        * @return {boolean}
         */
        matchAttribute: function ( objects, attrName ) {
-               var i, element, value;
-
-               // Check levels
-               value = null;
-               for ( i = 0; i < objects.length; i++ ) {
-                       element = objects[i];
-                       if ( value === null ) {
-                               value = element[attrName];
-                       } else {
-                               if ( value !== element[attrName] ) {
-                                       return false;
-                               }
-                       }
-               }
-               return true;
+               return $.map( objects, function ( object ) {
+                       return object[attrName];
+               } ).filter( function ( item, index, a ) {
+                       return index === a.indexOf( item );
+               } ).length === 1;
        },
 
        /**
         * Are all actions protected at the same level, with the same expiry time?
         *
-        * @return boolean
+        * @return {boolean}
         */
        areAllTypesMatching: function () {
-               return this.existingMatch
-                       && this.matchAttribute( this.getLevelSelectors(), 'selectedIndex' )
+               return this.matchAttribute( this.getLevelSelectors(), 'selectedIndex' )
                        && this.matchAttribute( this.getExpirySelectors(), 'selectedIndex' )
                        && this.matchAttribute( this.getExpiryInputs(), 'value' );
        },
@@ -217,7 +165,7 @@ var ProtectionForm = window.ProtectionForm = {
        /**
         * Is protection chaining off?
         *
-        * @return bool
+        * @return {boolean}
         */
        isUnchained: function () {
                var element = document.getElementById( 'mwProtectUnchained' );
@@ -228,160 +176,65 @@ var ProtectionForm = window.ProtectionForm = {
 
        /**
         * Find the highest protection level in any selector
+        * @return {number}
         */
        getMaxLevel: function () {
-               var maxIndex = -1;
-               this.forEachLevelSelector( function ( element ) {
-                       if ( element.selectedIndex > maxIndex ) {
-                               maxIndex = element.selectedIndex;
-                       }
-               } );
-               return maxIndex;
+               return Math.max.apply( Math, this.getLevelSelectors().map( function () {
+                       return this.selectedIndex;
+               } ) );
        },
 
        /**
         * Protect all actions at the specified level
         *
-        * @param index int Protection level
+        * @param {number} index Protection level
         */
        setAllSelectors: function ( index ) {
-               this.forEachLevelSelector( function ( element ) {
-                       if ( element.selectedIndex !== index ) {
-                               element.selectedIndex = index;
-                       }
+               this.getLevelSelectors().each( function () {
+                       this.selectedIndex = index;
                } );
        },
 
-       /**
-        * Apply a callback to each protection selector
-        *
-        * @param func callable Callback function
-        */
-       forEachLevelSelector: function ( func ) {
-               var i, selectors;
-
-               selectors = this.getLevelSelectors();
-               for ( i = 0; i < selectors.length; i++ ) {
-                       func( selectors[i] );
-               }
-       },
-
        /**
         * Get a list of all protection selectors on the page
         *
-        * @return Array
+        * @return {jQuery}
         */
        getLevelSelectors: function () {
-               var i, ours, all, element;
-
-               all = document.getElementsByTagName( 'select' );
-               ours = [];
-               for ( i = 0; i < all.length; i++ ) {
-                       element = all[i];
-                       if ( element.id.match( /^mwProtect-level-/ ) ) {
-                               ours[ours.length] = element;
-                       }
-               }
-               return ours;
-       },
-
-       /**
-        * Apply a callback to each expiry input
-        *
-        * @param func callable Callback function
-        */
-       forEachExpiryInput: function ( func ) {
-               var i, inputs;
-
-               inputs = this.getExpiryInputs();
-               for ( i = 0; i < inputs.length; i++ ) {
-                       func( inputs[i] );
-               }
+               return $( 'select[id ^= mwProtect-level-]' );
        },
 
        /**
         * Get a list of all expiry inputs on the page
         *
-        * @return Array
+        * @return {jQuery}
         */
        getExpiryInputs: function () {
-               var i, all, element, ours;
-
-               all = document.getElementsByTagName( 'input' );
-               ours = [];
-               for ( i = 0; i < all.length; i++ ) {
-                       element = all[i];
-                       if ( element.name.match( /^mwProtect-expiry-/ ) ) {
-                               ours[ours.length] = element;
-                       }
-               }
-               return ours;
-       },
-
-       /**
-        * Apply a callback to each expiry selector list
-        * @param func callable Callback function
-        */
-       forEachExpirySelector: function ( func ) {
-               var i, inputs;
-
-               inputs = this.getExpirySelectors();
-               for ( i = 0; i < inputs.length; i++ ) {
-                       func( inputs[i] );
-               }
+               return $( 'input[id ^= mwProtect-][id $= -expires]' );
        },
 
        /**
         * Get a list of all expiry selector lists on the page
         *
-        * @return Array
+        * @return {jQuery}
         */
        getExpirySelectors: function () {
-               var i, all, ours, element;
-
-               all = document.getElementsByTagName( 'select' );
-               ours = [];
-               for ( i = 0; i < all.length; i++ ) {
-                       element = all[i];
-                       if ( element.id.match( /^mwProtectExpirySelection-/ ) ) {
-                               ours[ours.length] = element;
-                       }
-               }
-               return ours;
+               return $( 'select[id ^= mwProtectExpirySelection-]' );
        },
 
        /**
         * Enable/disable protection selectors and expiry inputs
         *
-        * @param val boolean Enable?
+        * @param {boolean} val Enable?
         */
-       enableUnchainedInputs: function ( val ) {
-               var first = true;
-
-               this.forEachLevelSelector( function ( element ) {
-                       if ( first ) {
-                               first = false;
-                       } else {
-                               element.disabled = !val;
-                       }
-               } );
-               first = true;
-               this.forEachExpiryInput( function ( element ) {
-                       if ( first ) {
-                               first = false;
-                       } else {
-                               element.disabled = !val;
-                       }
-               } );
-               first = true;
-               this.forEachExpirySelector( function ( element ) {
-                       if ( first ) {
-                               first = false;
-                       } else {
-                               element.disabled = !val;
-                       }
-               } );
+       toggleUnchainedInputs: function ( val ) {
+               var setDisabled = function () { this.disabled = !val; };
+               this.getLevelSelectors().slice( 1 ).each( setDisabled );
+               this.getExpiryInputs().slice( 1 ).each( setDisabled );
+               this.getExpirySelectors().slice( 1 ).each( setDisabled );
        }
 };
 
+$( ProtectionForm.init.bind( ProtectionForm ) );
+
 }( mediaWiki, jQuery ) );