(bug 19524) Move hiding initially hidden collapsable tables from monobook/main.css...
[lhc/web/wiklou.git] / skins / common / protect.js
index 7641236..d9650c8 100644 (file)
-function protectInitialize(tableId, labelText) {
-       if (document.createTextNode) {
-               var box = document.getElementById(tableId);
-               if (!box)
+
+var ProtectionForm = {
+       'existingMatch': false,
+
+       /**
+        * Set up the protection chaining interface (i.e. "unlock move permissions" checkbox)
+        * on the protection form
+        *
+        * @param Object opts : 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 ) {
+               if( !( document.createTextNode && document.getElementById && document.getElementsByTagName ) )
+                       return false;
+
+               var box = document.getElementById( opts.tableId );
+               if( !box )
                        return false;
+               
+               var boxbody = box.getElementsByTagName('tbody')[0]
+               var row = document.createElement( 'tr' );
+               boxbody.insertBefore( row, boxbody.firstChild );
+
+               this.existingMatch = opts.existingMatch;
 
-               var tbody = box.getElementsByTagName('tbody')[0];
-               var row = document.createElement('tr');
-               tbody.appendChild(row);
+               var cell = document.createElement( 'td' );
+               row.appendChild( cell );
+               // If there is only one protection type, there is nothing to chain
+               if( opts.numTypes > 1 ) {
+                       var check = document.createElement( 'input' );
+                       check.id = 'mwProtectUnchained';
+                       check.type = 'checkbox';
+                       cell.appendChild( check );
+                       addClickHandler( check, function() { ProtectionForm.onChainClick(); } );
 
-               row.appendChild(document.createElement('td'));
-               var col2 = document.createElement('td');
-               row.appendChild(col2);
+                       cell.appendChild( document.createTextNode( ' ' ) );
+                       var label = document.createElement( 'label' );
+                       label.htmlFor = 'mwProtectUnchained';
+                       label.appendChild( document.createTextNode( opts.labelText ) );
+                       cell.appendChild( label );
+
+                       check.checked = !this.areAllTypesMatching();
+                       this.enableUnchainedInputs( check.checked );
+               }
 
-               var check = document.createElement('input');
-               check.id = "mwProtectUnchained";
-               check.type = "checkbox";
-               check.onclick = protectChainUpdate;
-               col2.appendChild(check);
+               this.updateCascadeCheckbox();
 
-               var space = document.createTextNode(" ");
-               col2.appendChild(space);
+               return true;
+       },
 
-               var label = document.createElement('label');
-               label.setAttribute("for", "mwProtectUnchained");
-               label.appendChild(document.createTextNode(labelText));
-               col2.appendChild(label);
+       /**
+        * Sets the disabled attribute on the cascade checkbox depending on the current selected levels
+        */
+       'updateCascadeCheckbox': function() {
+               // For non-existent titles, there is no cascade option
+               if( !document.getElementById( 'mwProtect-cascade' ) ) {
+                       return;
+               }
+               var lists = this.getLevelSelectors();
+               for( var i = 0; i < lists.length; i++ ) {
+                       if( lists[i].selectedIndex > -1 ) {
+                               var items = lists[i].getElementsByTagName( 'option' );
+                               var selected = items[ lists[i].selectedIndex ].value;
+                               if( !this.isCascadeableLevel(selected) ) {
+                                       document.getElementById( 'mwProtect-cascade' ).checked = false;
+                                       document.getElementById( 'mwProtect-cascade' ).disabled = true;
+                                       return;
+                               }
+                       }
+               }
+               document.getElementById( 'mwProtect-cascade' ).disabled = false;
+       },
 
-               if (protectAllMatch()) {
-                       check.checked = false;
-                       protectEnable(false);
+       /**
+        * Is this protection level cascadeable?
+        * @param String level
+        *
+        * @return boolean
+        *
+        */
+       'isCascadeableLevel': function( level ) {
+               for (var k = 0; k < wgCascadeableLevels.length; k++) {
+                       if ( wgCascadeableLevels[k] == level ) {
+                               return true;
+                       }
+               }
+               return false;
+       },
+
+       /**
+        * When protection levels are locked together, update the rest
+        * when one action's level changes
+        *
+        * @param Element source Level selector that changed
+        */
+       'updateLevels': function(source) {
+               if( !this.isUnchained() )
+                       this.setAllSelectors( source.selectedIndex );
+               this.updateCascadeCheckbox();
+       },
+
+       /**
+        * When protection levels are locked together, update the
+        * expiries when one changes
+        *
+        * @param Element source expiry input that changed
+        */
+
+       'updateExpiry': function(source) {
+               if( !this.isUnchained() ) {
+                       var expiry = source.value;
+                       this.forEachExpiryInput(function(element) {
+                               element.value = expiry;
+                       });
+               }
+               var listId = source.id.replace( /^mwProtect-(\w+)-expires$/, 'mwProtectExpirySelection-$1' );
+               var list = document.getElementById( listId );
+               if (list && list.value != 'othertime' ) {
+                       if ( this.isUnchained() ) {
+                               list.value = 'othertime';
+                       } else {
+                               this.forEachExpirySelector(function(element) {
+                                       element.value = 'othertime';
+                               });
+                       }
+               }
+       },
+
+       /**
+        * When protection levels are locked together, update the
+        * expiry lists when one changes and clear the custom inputs
+        *
+        * @param Element source expiry selector that changed
+        */
+       'updateExpiryList': function(source) {
+               if( !this.isUnchained() ) {
+                       var expiry = source.value;
+                       this.forEachExpirySelector(function(element) {
+                               element.value = expiry;
+                       });
+                       this.forEachExpiryInput(function(element) {
+                               element.value = '';
+                       });
+               }
+       },
+
+       /**
+        * Update chain status and enable/disable various bits of the UI
+        * when the user changes the "unlock move permissions" checkbox
+        */
+       'onChainClick': function() {
+               if( this.isUnchained() ) {
+                       this.enableUnchainedInputs( true );
                } else {
-                       check.checked = true;
-                       protectEnable(true);
+                       this.setAllSelectors( this.getMaxLevel() );
+                       this.enableUnchainedInputs( false );
                }
-               
-               allowCascade();
+               this.updateCascadeCheckbox();
+       },
 
-               return true;
-       }
-       return false;
-}
+       /**
+        * Returns true if the named attribute in all objects in the given array are matching
+        */
+       'matchAttribute' : function( objects, attrName ) {
+               var value = null;
 
-function allowCascade() {
-       var pr_types = document.getElementsByTagName("select");
-       for (var i = 0; i < pr_types.length; i++) {
-               if (pr_types[i].id.match(/^mwProtect-level-/)) {
-                       var selected_level = pr_types[i].getElementsByTagName("option")[pr_types[i].selectedIndex].value;
-                       for (var k=0; k < wgCascadeableLevels.length; k++) {
-                               if ( wgCascadeableLevels[k] != selected_level ) {
-                                       document.getElementById('mwProtect-cascade').checked=false;
-                                       document.getElementById('mwProtect-cascade').disabled=true;
+               // Check levels
+               for ( var i = 0; i < objects.length; i++ ) {
+                       var element = objects[i];
+                       if ( value == null ) {
+                               value = element[attrName];
+                       } else {
+                               if ( value != element[attrName] ) {
                                        return false;
                                }
                        }
                }
-       }
-       document.getElementById('mwProtect-cascade').disabled=false;
-       return true;
-}
+               return true;
+       },
 
-function protectLevelsUpdate(source) {
-       if (!protectUnchained()) {
-               protectUpdateAll(source.selectedIndex);
-       }
-       allowCascade();
-}
+       /**
+        * Are all actions protected at the same level, with the same expiry time?
+        *
+        * @return boolean
+        */
+       'areAllTypesMatching': function() {
+               return this.existingMatch
+                       && this.matchAttribute( this.getLevelSelectors(), 'selectedIndex' )
+                       && this.matchAttribute( this.getExpirySelectors(), 'selectedIndex' )
+                       && this.matchAttribute( this.getExpiryInputs(), 'value' );
+       },
 
-function protectChainUpdate() {
-       if (protectUnchained()) {
-               protectEnable(true);
-       } else {
-               protectChain();
-               protectEnable(false);
-       }
-}
+       /**
+        * Is protection chaining off?
+        *
+        * @return bool
+        */
+       'isUnchained': function() {
+               var element = document.getElementById( 'mwProtectUnchained' );
+               return element
+                       ? element.checked
+                       : true; // No control, so we need to let the user set both levels
+       },
 
+       /**
+        * Find the highest protection level in any selector
+        */
+       'getMaxLevel': function() {
+               var maxIndex = -1;
+               this.forEachLevelSelector(function(element) {
+                       if (element.selectedIndex > maxIndex) {
+                               maxIndex = element.selectedIndex;
+                       }
+               });
+               return maxIndex;
+       },
 
-function protectAllMatch() {
-       var values = new Array();
-       protectForSelectors(function(set) {
-               values[values.length] = set.selectedIndex;
-       });
-       for (var i = 1; i < values.length; i++) {
-               if (values[i] != values[0]) {
-                       return false;
-               }
-       }
-       return true;
-}
+       /**
+        * Protect all actions at the specified level
+        *
+        * @param int index Protection level
+        */
+       'setAllSelectors': function(index) {
+               this.forEachLevelSelector(function(element) {
+                       if (element.selectedIndex != index) {
+                               element.selectedIndex = index;
+                       }
+               });
+       },
 
-function protectUnchained() {
-       var unchain = document.getElementById("mwProtectUnchained");
-       if (!unchain) {
-               alert("This shouldn't happen");
-               return false;
-       }
-       return unchain.checked;
-}
+       /**
+        * Apply a callback to each protection selector
+        *
+        * @param callable func Callback function
+        */
+       'forEachLevelSelector': function(func) {
+               var selectors = this.getLevelSelectors();
+               for (var i = 0; i < selectors.length; i++) {
+                       func(selectors[i]);
+               }
+       },
 
-function protectChain() {
-       // Find the highest-protected action and bump them all to this level
-       var maxIndex = -1;
-       protectForSelectors(function(set) {
-               if (set.selectedIndex > maxIndex) {
-                       maxIndex = set.selectedIndex;
+       /**
+        * Get a list of all protection selectors on the page
+        *
+        * @return Array
+        */
+       'getLevelSelectors': function() {
+               var all = document.getElementsByTagName("select");
+               var ours = new Array();
+               for (var i = 0; i < all.length; i++) {
+                       var element = all[i];
+                       if (element.id.match(/^mwProtect-level-/)) {
+                               ours[ours.length] = element;
+                       }
                }
-       });
-       protectUpdateAll(maxIndex);
-}
+               return ours;
+       },
 
-function protectUpdateAll(index) {
-       protectForSelectors(function(set) {
-               if (set.selectedIndex != index) {
-                       set.selectedIndex = index;
+       /**
+        * Apply a callback to each expiry input
+        *
+        * @param callable func Callback function
+        */
+       'forEachExpiryInput': function(func) {
+               var inputs = this.getExpiryInputs();
+               for (var i = 0; i < inputs.length; i++) {
+                       func(inputs[i]);
                }
-       });
-}
+       },
 
-function protectForSelectors(func) {
-       var selectors = protectSelectors();
-       for (var i = 0; i < selectors.length; i++) {
-               func(selectors[i]);
-       }
-}
+       /**
+        * Get a list of all expiry inputs on the page
+        *
+        * @return Array
+        */
+       'getExpiryInputs': function() {
+               var all = document.getElementsByTagName("input");
+               var ours = new Array();
+               for (var i = 0; i < all.length; i++) {
+                       var element = all[i];
+                       if (element.name.match(/^mwProtect-expiry-/)) {
+                               ours[ours.length] = element;
+                       }
+               }
+               return ours;
+       },
 
-function protectSelectors() {
-       var all = document.getElementsByTagName("select");
-       var ours = new Array();
-       for (var i = 0; i < all.length; i++) {
-               var set = all[i];
-               if (set.id.match(/^mwProtect-level-/)) {
-                       ours[ours.length] = set;
+       /**
+        * Apply a callback to each expiry selector list
+        * @param callable func Callback function
+        */
+       'forEachExpirySelector': function(func) {
+               var inputs = this.getExpirySelectors();
+               for (var i = 0; i < inputs.length; i++) {
+                       func(inputs[i]);
                }
-       }
-       return ours;
-}
+       },
 
-function protectEnable(val) {
-       // fixme
-       var first = true;
-       protectForSelectors(function(set) {
-               if (first) {
-                       first = false;
-               } else {
-                       set.disabled = !val;
-                       set.style.visible = val ? "visible" : "hidden";
+       /**
+        * Get a list of all expiry selector lists on the page
+        *
+        * @return Array
+        */
+       'getExpirySelectors': function() {
+               var all = document.getElementsByTagName("select");
+               var ours = new Array();
+               for (var i = 0; i < all.length; i++) {
+                       var element = all[i];
+                       if (element.id.match(/^mwProtectExpirySelection-/)) {
+                               ours[ours.length] = element;
+                       }
                }
-       });
+               return ours;
+       },
+
+       /**
+        * Enable/disable protection selectors and expiry inputs
+        *
+        * @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;
+                       }
+               });
+       }
 }