3 var ProtectionForm
= window
.ProtectionForm
= {
5 * Set up the protection chaining interface (i.e. "unlock move permissions" checkbox)
6 * on the protection form
9 var $cell
= $( '<td>' ),
10 $row
= $( '<tr>' ).append( $cell
);
12 if ( !$( '#mwProtectSet' ).length
) {
16 if ( mw
.config
.get( 'wgCascadeableLevels' ) !== undefined ) {
17 $( 'form#mw-Protect-Form' ).submit( this.toggleUnchainedInputs
.bind( ProtectionForm
, true ) );
19 this.getExpirySelectors().each( function () {
20 $( this ).change( ProtectionForm
.updateExpiryList
.bind( ProtectionForm
, this ) );
22 this.getExpiryInputs().each( function () {
23 $( this ).on( 'keyup change', ProtectionForm
.updateExpiry
.bind( ProtectionForm
, this ) );
25 this.getLevelSelectors().each( function () {
26 $( this ).change( ProtectionForm
.updateLevels
.bind( ProtectionForm
, this ) );
29 $( '#mwProtectSet > tbody > tr:first' ).after( $row
);
31 // If there is only one protection type, there is nothing to chain
32 if ( $( '[id ^= mw-protect-table-]' ).length
> 1 ) {
35 .attr( { id
: 'mwProtectUnchained', type
: 'checkbox' } )
36 .click( this.onChainClick
.bind( this ) )
37 .prop( 'checked', !this.areAllTypesMatching() ),
38 document
.createTextNode( ' ' ),
40 .attr( 'for', 'mwProtectUnchained' )
41 .text( mw
.msg( 'protect-unchain-permissions' ) )
44 this.toggleUnchainedInputs( !this.areAllTypesMatching() );
47 $( '#mwProtect-reason' ).byteLimit( 180 );
49 this.updateCascadeCheckbox();
53 * Sets the disabled attribute on the cascade checkbox depending on the current selected levels
55 updateCascadeCheckbox: function () {
56 this.getLevelSelectors().each( function () {
57 if ( !ProtectionForm
.isCascadeableLevel( $( this ).val() ) ) {
58 $( '#mwProtect-cascade' ).prop( { checked
: false, disabled
: true } );
61 $( '#mwProtect-cascade' ).prop( 'disabled', false );
67 * Checks if a certain protection level is cascadeable.
69 * @param {string} level
72 isCascadeableLevel: function ( level
) {
73 return $.inArray( level
, mw
.config
.get( 'wgCascadeableLevels' ) ) !== -1;
77 * When protection levels are locked together, update the rest
78 * when one action's level changes
80 * @param {Element} source Level selector that changed
82 updateLevels: function ( source
) {
83 if ( !this.isUnchained() ) {
84 this.setAllSelectors( source
.selectedIndex
);
86 this.updateCascadeCheckbox();
90 * When protection levels are locked together, update the
91 * expiries when one changes
93 * @param {Element} source expiry input that changed
96 updateExpiry: function ( source
) {
97 if ( !this.isUnchained() ) {
98 this.getExpiryInputs().each( function () {
99 this.value
= source
.value
;
102 if ( this.isUnchained() ) {
103 $( '#' + source
.id
.replace( /^mwProtect-(\w+)-expires$/, 'mwProtectExpirySelection-$1' ) ).val( 'othertime' );
105 this.getExpirySelectors().each( function () {
106 this.value
= 'othertime';
112 * When protection levels are locked together, update the
113 * expiry lists when one changes and clear the custom inputs
115 * @param {Element} source Expiry selector that changed
117 updateExpiryList: function ( source
) {
118 if ( !this.isUnchained() ) {
119 this.getExpirySelectors().each( function () {
120 this.value
= source
.value
;
122 this.getExpiryInputs().each( function () {
129 * Update chain status and enable/disable various bits of the UI
130 * when the user changes the "unlock move permissions" checkbox
132 onChainClick: function () {
133 this.toggleUnchainedInputs( this.isUnchained() );
134 if ( !this.isUnchained() ) {
135 this.setAllSelectors( this.getMaxLevel() );
137 this.updateCascadeCheckbox();
141 * Returns true if the named attribute in all objects in the given array are matching
143 * @param {Object[]} objects
144 * @param {string} attrName
147 matchAttribute: function ( objects
, attrName
) {
148 return $.map( objects
, function ( object
) {
149 return object
[ attrName
];
150 } ).filter( function ( item
, index
, a
) {
151 return index
=== a
.indexOf( item
);
156 * Are all actions protected at the same level, with the same expiry time?
160 areAllTypesMatching: function () {
161 return this.matchAttribute( this.getLevelSelectors(), 'selectedIndex' )
162 && this.matchAttribute( this.getExpirySelectors(), 'selectedIndex' )
163 && this.matchAttribute( this.getExpiryInputs(), 'value' );
167 * Is protection chaining off?
171 isUnchained: function () {
172 var element
= document
.getElementById( 'mwProtectUnchained' );
175 : true; // No control, so we need to let the user set both levels
179 * Find the highest protection level in any selector
183 getMaxLevel: function () {
184 return Math
.max
.apply( Math
, this.getLevelSelectors().map( function () {
185 return this.selectedIndex
;
190 * Protect all actions at the specified level
192 * @param {number} index Protection level
194 setAllSelectors: function ( index
) {
195 this.getLevelSelectors().each( function () {
196 this.selectedIndex
= index
;
201 * Get a list of all protection selectors on the page
205 getLevelSelectors: function () {
206 return $( 'select[id ^= mwProtect-level-]' );
210 * Get a list of all expiry inputs on the page
214 getExpiryInputs: function () {
215 return $( 'input[id ^= mwProtect-][id $= -expires]' );
219 * Get a list of all expiry selector lists on the page
223 getExpirySelectors: function () {
224 return $( 'select[id ^= mwProtectExpirySelection-]' );
228 * Enable/disable protection selectors and expiry inputs
230 * @param {boolean} val Enable?
232 toggleUnchainedInputs: function ( val
) {
233 var setDisabled = function () { this.disabled
= !val
; };
234 this.getLevelSelectors().slice( 1 ).each( setDisabled
);
235 this.getExpiryInputs().slice( 1 ).each( setDisabled
);
236 this.getExpirySelectors().slice( 1 ).each( setDisabled
);
240 $( ProtectionForm
.init
.bind( ProtectionForm
) );
242 }( mediaWiki
, jQuery
) );