Merge "Drop zh-tw message "saveprefs""
[lhc/web/wiklou.git] / resources / src / mediawiki.legacy / protect.js
1 ( function ( mw, $ ) {
2
3 var ProtectionForm = window.ProtectionForm = {
4 /**
5 * Set up the protection chaining interface (i.e. "unlock move permissions" checkbox)
6 * on the protection form
7 */
8 init: function () {
9 var $cell = $( '<td>' ),
10 $row = $( '<tr>' ).append( $cell );
11
12 if ( !$( '#mwProtectSet' ).length ) {
13 return false;
14 }
15
16 if ( mw.config.get( 'wgCascadeableLevels' ) !== undefined ) {
17 $( 'form#mw-Protect-Form' ).submit( this.toggleUnchainedInputs.bind( ProtectionForm, true ) );
18 }
19 this.getExpirySelectors().each( function () {
20 $( this ).change( ProtectionForm.updateExpiryList.bind( ProtectionForm, this ) );
21 } );
22 this.getExpiryInputs().each( function () {
23 $( this ).on( 'keyup change', ProtectionForm.updateExpiry.bind( ProtectionForm, this ) );
24 } );
25 this.getLevelSelectors().each( function () {
26 $( this ).change( ProtectionForm.updateLevels.bind( ProtectionForm, this ) );
27 } );
28
29 $( '#mwProtectSet > tbody > tr:first' ).after( $row );
30
31 // If there is only one protection type, there is nothing to chain
32 if ( $( '[id ^= mw-protect-table-]' ).length > 1 ) {
33 $cell.append(
34 $( '<input>' )
35 .attr( { id: 'mwProtectUnchained', type: 'checkbox' } )
36 .click( this.onChainClick.bind( this ) )
37 .prop( 'checked', !this.areAllTypesMatching() ),
38 document.createTextNode( ' ' ),
39 $( '<label>' )
40 .attr( 'for', 'mwProtectUnchained' )
41 .text( mw.msg( 'protect-unchain-permissions' ) )
42 );
43
44 this.toggleUnchainedInputs( !this.areAllTypesMatching() );
45 }
46
47 $( '#mwProtect-reason' ).byteLimit( 180 );
48
49 this.updateCascadeCheckbox();
50 },
51
52 /**
53 * Sets the disabled attribute on the cascade checkbox depending on the current selected levels
54 */
55 updateCascadeCheckbox: function () {
56 this.getLevelSelectors().each( function () {
57 if ( !ProtectionForm.isCascadeableLevel( $( this ).val() ) ) {
58 $( '#mwProtect-cascade' ).prop( { checked: false, disabled: true } );
59 return false;
60 } else {
61 $( '#mwProtect-cascade' ).prop( 'disabled', false );
62 }
63 } );
64 },
65
66 /**
67 * Checks if a certain protection level is cascadeable.
68 *
69 * @param {string} level
70 * @return {boolean}
71 */
72 isCascadeableLevel: function ( level ) {
73 return $.inArray( level, mw.config.get( 'wgCascadeableLevels' ) ) !== -1;
74 },
75
76 /**
77 * When protection levels are locked together, update the rest
78 * when one action's level changes
79 *
80 * @param {Element} source Level selector that changed
81 */
82 updateLevels: function ( source ) {
83 if ( !this.isUnchained() ) {
84 this.setAllSelectors( source.selectedIndex );
85 }
86 this.updateCascadeCheckbox();
87 },
88
89 /**
90 * When protection levels are locked together, update the
91 * expiries when one changes
92 *
93 * @param {Element} source expiry input that changed
94 */
95
96 updateExpiry: function ( source ) {
97 if ( !this.isUnchained() ) {
98 this.getExpiryInputs().each( function () {
99 this.value = source.value;
100 } );
101 }
102 if ( this.isUnchained() ) {
103 $( '#' + source.id.replace( /^mwProtect-(\w+)-expires$/, 'mwProtectExpirySelection-$1' ) ).val( 'othertime' );
104 } else {
105 this.getExpirySelectors().each( function () {
106 this.value = 'othertime';
107 } );
108 }
109 },
110
111 /**
112 * When protection levels are locked together, update the
113 * expiry lists when one changes and clear the custom inputs
114 *
115 * @param {Element} source Expiry selector that changed
116 */
117 updateExpiryList: function ( source ) {
118 if ( !this.isUnchained() ) {
119 this.getExpirySelectors().each( function () {
120 this.value = source.value;
121 } );
122 this.getExpiryInputs().each( function () {
123 this.value = '';
124 } );
125 }
126 },
127
128 /**
129 * Update chain status and enable/disable various bits of the UI
130 * when the user changes the "unlock move permissions" checkbox
131 */
132 onChainClick: function () {
133 this.toggleUnchainedInputs( this.isUnchained() );
134 if ( !this.isUnchained() ) {
135 this.setAllSelectors( this.getMaxLevel() );
136 }
137 this.updateCascadeCheckbox();
138 },
139
140 /**
141 * Returns true if the named attribute in all objects in the given array are matching
142 *
143 * @param {Object[]} objects
144 * @param {string} attrName
145 * @return {boolean}
146 */
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 );
152 } ).length === 1;
153 },
154
155 /**
156 * Are all actions protected at the same level, with the same expiry time?
157 *
158 * @return {boolean}
159 */
160 areAllTypesMatching: function () {
161 return this.matchAttribute( this.getLevelSelectors(), 'selectedIndex' )
162 && this.matchAttribute( this.getExpirySelectors(), 'selectedIndex' )
163 && this.matchAttribute( this.getExpiryInputs(), 'value' );
164 },
165
166 /**
167 * Is protection chaining off?
168 *
169 * @return {boolean}
170 */
171 isUnchained: function () {
172 var element = document.getElementById( 'mwProtectUnchained' );
173 return element
174 ? element.checked
175 : true; // No control, so we need to let the user set both levels
176 },
177
178 /**
179 * Find the highest protection level in any selector
180 *
181 * @return {number}
182 */
183 getMaxLevel: function () {
184 return Math.max.apply( Math, this.getLevelSelectors().map( function () {
185 return this.selectedIndex;
186 } ) );
187 },
188
189 /**
190 * Protect all actions at the specified level
191 *
192 * @param {number} index Protection level
193 */
194 setAllSelectors: function ( index ) {
195 this.getLevelSelectors().each( function () {
196 this.selectedIndex = index;
197 } );
198 },
199
200 /**
201 * Get a list of all protection selectors on the page
202 *
203 * @return {jQuery}
204 */
205 getLevelSelectors: function () {
206 return $( 'select[id ^= mwProtect-level-]' );
207 },
208
209 /**
210 * Get a list of all expiry inputs on the page
211 *
212 * @return {jQuery}
213 */
214 getExpiryInputs: function () {
215 return $( 'input[id ^= mwProtect-][id $= -expires]' );
216 },
217
218 /**
219 * Get a list of all expiry selector lists on the page
220 *
221 * @return {jQuery}
222 */
223 getExpirySelectors: function () {
224 return $( 'select[id ^= mwProtectExpirySelection-]' );
225 },
226
227 /**
228 * Enable/disable protection selectors and expiry inputs
229 *
230 * @param {boolean} val Enable?
231 */
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 );
237 }
238 };
239
240 $( ProtectionForm.init.bind( ProtectionForm ) );
241
242 }( mediaWiki, jQuery ) );