protect.js 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352
  1. var ProtectionForm = {
  2. 'existingMatch': false,
  3. /**
  4. * Set up the protection chaining interface (i.e. "unlock move permissions" checkbox)
  5. * on the protection form
  6. *
  7. * @param Object opts : parameters with members:
  8. * tableId Identifier of the table containing UI bits
  9. * labelText Text to use for the checkbox label
  10. * numTypes The number of protection types
  11. * existingMatch True if all the existing expiry times match
  12. */
  13. 'init': function( opts ) {
  14. if( !( document.createTextNode && document.getElementById && document.getElementsByTagName ) )
  15. return false;
  16. var box = document.getElementById( opts.tableId );
  17. if( !box )
  18. return false;
  19. var boxbody = box.getElementsByTagName('tbody')[0]
  20. var row = document.createElement( 'tr' );
  21. boxbody.insertBefore( row, boxbody.firstChild );
  22. this.existingMatch = opts.existingMatch;
  23. var cell = document.createElement( 'td' );
  24. row.appendChild( cell );
  25. // If there is only one protection type, there is nothing to chain
  26. if( opts.numTypes > 1 ) {
  27. var check = document.createElement( 'input' );
  28. check.id = 'mwProtectUnchained';
  29. check.type = 'checkbox';
  30. cell.appendChild( check );
  31. addClickHandler( check, function() { ProtectionForm.onChainClick(); } );
  32. cell.appendChild( document.createTextNode( ' ' ) );
  33. var label = document.createElement( 'label' );
  34. label.htmlFor = 'mwProtectUnchained';
  35. label.appendChild( document.createTextNode( opts.labelText ) );
  36. cell.appendChild( label );
  37. check.checked = !this.areAllTypesMatching();
  38. this.enableUnchainedInputs( check.checked );
  39. }
  40. this.updateCascadeCheckbox();
  41. return true;
  42. },
  43. /**
  44. * Sets the disabled attribute on the cascade checkbox depending on the current selected levels
  45. */
  46. 'updateCascadeCheckbox': function() {
  47. // For non-existent titles, there is no cascade option
  48. if( !document.getElementById( 'mwProtect-cascade' ) ) {
  49. return;
  50. }
  51. var lists = this.getLevelSelectors();
  52. for( var i = 0; i < lists.length; i++ ) {
  53. if( lists[i].selectedIndex > -1 ) {
  54. var items = lists[i].getElementsByTagName( 'option' );
  55. var selected = items[ lists[i].selectedIndex ].value;
  56. if( !this.isCascadeableLevel(selected) ) {
  57. document.getElementById( 'mwProtect-cascade' ).checked = false;
  58. document.getElementById( 'mwProtect-cascade' ).disabled = true;
  59. return;
  60. }
  61. }
  62. }
  63. document.getElementById( 'mwProtect-cascade' ).disabled = false;
  64. },
  65. /**
  66. * Is this protection level cascadeable?
  67. * @param String level
  68. *
  69. * @return boolean
  70. *
  71. */
  72. 'isCascadeableLevel': function( level ) {
  73. for (var k = 0; k < wgCascadeableLevels.length; k++) {
  74. if ( wgCascadeableLevels[k] == level ) {
  75. return true;
  76. }
  77. }
  78. return false;
  79. },
  80. /**
  81. * When protection levels are locked together, update the rest
  82. * when one action's level changes
  83. *
  84. * @param Element source Level selector that changed
  85. */
  86. 'updateLevels': function(source) {
  87. if( !this.isUnchained() )
  88. this.setAllSelectors( source.selectedIndex );
  89. this.updateCascadeCheckbox();
  90. },
  91. /**
  92. * When protection levels are locked together, update the
  93. * expiries when one changes
  94. *
  95. * @param Element source expiry input that changed
  96. */
  97. 'updateExpiry': function(source) {
  98. if( !this.isUnchained() ) {
  99. var expiry = source.value;
  100. this.forEachExpiryInput(function(element) {
  101. element.value = expiry;
  102. });
  103. }
  104. var listId = source.id.replace( /^mwProtect-(\w+)-expires$/, 'mwProtectExpirySelection-$1' );
  105. var list = document.getElementById( listId );
  106. if (list && list.value != 'othertime' ) {
  107. if ( this.isUnchained() ) {
  108. list.value = 'othertime';
  109. } else {
  110. this.forEachExpirySelector(function(element) {
  111. element.value = 'othertime';
  112. });
  113. }
  114. }
  115. },
  116. /**
  117. * When protection levels are locked together, update the
  118. * expiry lists when one changes and clear the custom inputs
  119. *
  120. * @param Element source expiry selector that changed
  121. */
  122. 'updateExpiryList': function(source) {
  123. if( !this.isUnchained() ) {
  124. var expiry = source.value;
  125. this.forEachExpirySelector(function(element) {
  126. element.value = expiry;
  127. });
  128. this.forEachExpiryInput(function(element) {
  129. element.value = '';
  130. });
  131. }
  132. },
  133. /**
  134. * Update chain status and enable/disable various bits of the UI
  135. * when the user changes the "unlock move permissions" checkbox
  136. */
  137. 'onChainClick': function() {
  138. if( this.isUnchained() ) {
  139. this.enableUnchainedInputs( true );
  140. } else {
  141. this.setAllSelectors( this.getMaxLevel() );
  142. this.enableUnchainedInputs( false );
  143. }
  144. this.updateCascadeCheckbox();
  145. },
  146. /**
  147. * Returns true if the named attribute in all objects in the given array are matching
  148. */
  149. 'matchAttribute' : function( objects, attrName ) {
  150. var value = null;
  151. // Check levels
  152. for ( var i = 0; i < objects.length; i++ ) {
  153. var element = objects[i];
  154. if ( value == null ) {
  155. value = element[attrName];
  156. } else {
  157. if ( value != element[attrName] ) {
  158. return false;
  159. }
  160. }
  161. }
  162. return true;
  163. },
  164. /**
  165. * Are all actions protected at the same level, with the same expiry time?
  166. *
  167. * @return boolean
  168. */
  169. 'areAllTypesMatching': function() {
  170. return this.existingMatch
  171. && this.matchAttribute( this.getLevelSelectors(), 'selectedIndex' )
  172. && this.matchAttribute( this.getExpirySelectors(), 'selectedIndex' )
  173. && this.matchAttribute( this.getExpiryInputs(), 'value' );
  174. },
  175. /**
  176. * Is protection chaining off?
  177. *
  178. * @return bool
  179. */
  180. 'isUnchained': function() {
  181. var element = document.getElementById( 'mwProtectUnchained' );
  182. return element
  183. ? element.checked
  184. : true; // No control, so we need to let the user set both levels
  185. },
  186. /**
  187. * Find the highest protection level in any selector
  188. */
  189. 'getMaxLevel': function() {
  190. var maxIndex = -1;
  191. this.forEachLevelSelector(function(element) {
  192. if (element.selectedIndex > maxIndex) {
  193. maxIndex = element.selectedIndex;
  194. }
  195. });
  196. return maxIndex;
  197. },
  198. /**
  199. * Protect all actions at the specified level
  200. *
  201. * @param int index Protection level
  202. */
  203. 'setAllSelectors': function(index) {
  204. this.forEachLevelSelector(function(element) {
  205. if (element.selectedIndex != index) {
  206. element.selectedIndex = index;
  207. }
  208. });
  209. },
  210. /**
  211. * Apply a callback to each protection selector
  212. *
  213. * @param callable func Callback function
  214. */
  215. 'forEachLevelSelector': function(func) {
  216. var selectors = this.getLevelSelectors();
  217. for (var i = 0; i < selectors.length; i++) {
  218. func(selectors[i]);
  219. }
  220. },
  221. /**
  222. * Get a list of all protection selectors on the page
  223. *
  224. * @return Array
  225. */
  226. 'getLevelSelectors': function() {
  227. var all = document.getElementsByTagName("select");
  228. var ours = new Array();
  229. for (var i = 0; i < all.length; i++) {
  230. var element = all[i];
  231. if (element.id.match(/^mwProtect-level-/)) {
  232. ours[ours.length] = element;
  233. }
  234. }
  235. return ours;
  236. },
  237. /**
  238. * Apply a callback to each expiry input
  239. *
  240. * @param callable func Callback function
  241. */
  242. 'forEachExpiryInput': function(func) {
  243. var inputs = this.getExpiryInputs();
  244. for (var i = 0; i < inputs.length; i++) {
  245. func(inputs[i]);
  246. }
  247. },
  248. /**
  249. * Get a list of all expiry inputs on the page
  250. *
  251. * @return Array
  252. */
  253. 'getExpiryInputs': function() {
  254. var all = document.getElementsByTagName("input");
  255. var ours = new Array();
  256. for (var i = 0; i < all.length; i++) {
  257. var element = all[i];
  258. if (element.name.match(/^mwProtect-expiry-/)) {
  259. ours[ours.length] = element;
  260. }
  261. }
  262. return ours;
  263. },
  264. /**
  265. * Apply a callback to each expiry selector list
  266. * @param callable func Callback function
  267. */
  268. 'forEachExpirySelector': function(func) {
  269. var inputs = this.getExpirySelectors();
  270. for (var i = 0; i < inputs.length; i++) {
  271. func(inputs[i]);
  272. }
  273. },
  274. /**
  275. * Get a list of all expiry selector lists on the page
  276. *
  277. * @return Array
  278. */
  279. 'getExpirySelectors': function() {
  280. var all = document.getElementsByTagName("select");
  281. var ours = new Array();
  282. for (var i = 0; i < all.length; i++) {
  283. var element = all[i];
  284. if (element.id.match(/^mwProtectExpirySelection-/)) {
  285. ours[ours.length] = element;
  286. }
  287. }
  288. return ours;
  289. },
  290. /**
  291. * Enable/disable protection selectors and expiry inputs
  292. *
  293. * @param boolean val Enable?
  294. */
  295. 'enableUnchainedInputs': function(val) {
  296. var first = true;
  297. this.forEachLevelSelector(function(element) {
  298. if (first) {
  299. first = false;
  300. } else {
  301. element.disabled = !val;
  302. }
  303. });
  304. first = true;
  305. this.forEachExpiryInput(function(element) {
  306. if (first) {
  307. first = false;
  308. } else {
  309. element.disabled = !val;
  310. }
  311. });
  312. first = true;
  313. this.forEachExpirySelector(function(element) {
  314. if (first) {
  315. first = false;
  316. } else {
  317. element.disabled = !val;
  318. }
  319. });
  320. }
  321. }