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