۵٬۱۲۳
ویرایش
(صفحهای تازه حاوی «// <source lang="javascript"> // // Cat-A-Lot // Changes category of multiple files (or pages) // // Originally by Magnus Manske // RegExes...» ایجاد کرد) |
بدون خلاصۀ ویرایش |
||
خط ۱: | خط ۱: | ||
/ | /** | ||
* Cat-a-lot | |||
* Changes category of multiple files | |||
* | |||
* @rev 00:13, 10 February 2018 (UTC) | |||
* @author Originally by Magnus Manske (2007) | |||
* @author RegExes by Ilmari Karonen (2010) | |||
* @author Completely rewritten by DieBuche (2010-2012) | |||
* @author Rillke (2012-2014) | |||
* @author Perhelion (2017) | |||
* Requires [[MediaWiki:Gadget-SettingsManager.js]] and [[MediaWiki:Gadget-SettingsUI.js]] (properly registered) for per-user-settings | |||
* | |||
// | * READ THIS PAGE IF YOU WANT TO TRANSLATE OR USE THIS ON ANOTHER SITE: | ||
var | * http://commons.wikimedia.org/wiki/MediaWiki:Gadget-Cat-a-lot.js/translating | ||
* <nowiki> | |||
*/ | |||
/* global jQuery, mediaWiki */ | |||
/* eslint one-var:0, vars-on-top:0, no-underscore-dangle:0, valid-jsdoc:0, | |||
curly:0, camelcase:0, no-useless-escape:0, no-alert:0 */ // extends: wikimedia | |||
/* jshint unused:true, forin:false, smarttabs:true, loopfunc:true, browser:true */ | |||
( function ( $, mw ) { | |||
'use strict'; | |||
var formattedNS = mw.config.get( 'wgFormattedNamespaces' ), | |||
ns = mw.config.get( 'wgNamespaceNumber' ), | |||
nsIDs = mw.config.get( 'wgNamespaceIds' ), | |||
userGrp = mw.config.get( 'wgUserGroups' ), | |||
project = mw.config.get( 'wgDBname' ); | |||
var msgs = { | |||
// Preferences | |||
// new: added 2012-09-19. Please translate. | |||
// Use user language for i18n | |||
'cat-a-lot-watchlistpref': 'Watchlist preference concerning files edited with Cat-a-lot', | |||
'cat-a-lot-watch_pref': 'According to your general preferences', | |||
'cat-a-lot-watch_nochange': 'Do not change watchstatus', | |||
'cat-a-lot-watch_watch': 'Watch pages edited with Cat-a-lot', | |||
'cat-a-lot-watch_unwatch': 'Remove pages while editing with Cat-a-lot from your watchlist', | |||
'cat-a-lot-minorpref': 'Mark edits as minor (if you generally mark your edits as minor, this won’t change anything)', | |||
'cat-a-lot-editpagespref': 'Allow categorising pages (including categories) that are not files', | |||
'cat-a-lot-docleanuppref': 'Remove {{Check categories}} and other minor cleanup', | |||
'cat-a-lot-uncatpref': 'Remove {{Uncategorized}}', | |||
'cat-a-lot-subcatcountpref': 'Sub-categories to show at most', | |||
'cat-a-lot-config-settings': 'Preferences', | |||
'cat-a-lot-buttonpref': 'Use buttons instead of text-links', | |||
'cat-a-lot-comment-label': 'Custom edit comment', | |||
'cat-a-lot-edit-question': 'Why is this change necessary?', | |||
// Progress | |||
// 'cat-a-lot-loading': 'Loading …', | |||
'cat-a-lot-editing': 'Editing page', | |||
'cat-a-lot-of': 'of ', | |||
'cat-a-lot-skipped-already': 'The following {{PLURAL:$1|1=page was|$1 pages were}} skipped, because the page was already in the category:', | |||
'cat-a-lot-skipped-not-found': 'The following {{PLURAL:$1|1=page was|$1 pages were}} skipped, because the old category could not be found:', | |||
'cat-a-lot-skipped-server': 'The following {{PLURAL:$1|1=page|$1 pages}} couldn’t be changed, since there were problems connecting to the server:', | |||
'cat-a-lot-all-done': 'All pages are processed.', | |||
'cat-a-lot-done': 'Done!', // mw.msg("Feedback-close") | |||
'cat-a-lot-added-cat': 'Added category $1', | |||
'cat-a-lot-copied-cat': 'Copied to category $1', | |||
'cat-a-lot-moved-cat': 'Moved to category $1', | |||
'cat-a-lot-removed-cat': 'Removed from category $1', | |||
// 'cat-a-lot-return-to-page': 'Return to page', | |||
// 'cat-a-lot-cat-not-found': 'Category not found.', | |||
// as in 17 files selected | |||
'cat-a-lot-files-selected': '{{PLURAL:$1|1=One file|$1 files}} selected.', | |||
'cat-a-lot-pe_file': '$1 {{PLURAL:$1|page|pages}} of $2 affected', | |||
'cat-a-lot-parent-cat': 'Has parent-category: ', | |||
'cat-a-lot-sub-cat': 'Has sub-category: ', | |||
// Actions | |||
'cat-a-lot-copy': 'Copy', | |||
'cat-a-lot-move': 'Move', | |||
'cat-a-lot-add': 'Add', | |||
// 'cat-a-lot-remove-from-cat': 'Remove from this category', | |||
'cat-a-lot-overcat': 'Check over-categorization', | |||
'cat-a-lot-enter-name': 'Enter category name', | |||
'cat-a-lot-select': 'Select', | |||
'cat-a-lot-all': 'all', | |||
'cat-a-lot-none': 'none', | |||
// 'cat-a-lot-none-selected': 'No files selected!', 'Ooui-selectfile-placeholder' | |||
// Summaries (project language): | |||
'cat-a-lot-pref-save-summary': 'Updating user preferences', | |||
'cat-a-lot-summary-add': 'Adding [[Category:$1]]', | |||
'cat-a-lot-summary-copy': 'Copying from [[Category:$1]] to [[Category:$2]]', | |||
'cat-a-lot-summary-move': 'Moving from [[Category:$1]] to [[Category:$2]]', | |||
'cat-a-lot-summary-remove': 'Removing from [[Category:$1]]', | |||
'cat-a-lot-prefix-summary': '', | |||
'cat-a-lot-using-summary': ' using [[c:Help:Cat-a-lot|Cat-a-lot]]' | |||
}; | |||
mw.messages.set( msgs ); | |||
function msg( /* params */ ) { | |||
var args = Array.prototype.slice.call( arguments, 0 ); | |||
args[ 0 ] = 'cat-a-lot-' + args[ 0 ]; | |||
return ( args.length === 1 ) ? | |||
mw.message( args[ 0 ] ).plain() : | |||
mw.message.apply( mw.message, args ).parse(); | |||
} | |||
// There is only one Cat-a-lot on one page | |||
var $body, $container, $dataContainer, $searchInputContainer, $searchInput, $resultList, $markCounter, $selections, | |||
$selectFiles, $selectPages, $selectNone, $selectInvert, $settingsWrapper, $settingsLink, $head, $link, $overcat, | |||
commonsURL = 'https://commons.wikimedia.org/w/index.php', | |||
is_rtl = $( 'body' ).hasClass( 'rtl' ), | |||
reCat, // localized category search regexp | |||
non, | |||
r; // result file count for overcat | |||
var CAL = mw.libs.catALot = { | |||
apiUrl: mw.util.wikiScript( 'api' ), | |||
origin: '', | |||
searchmode: false, | searchmode: false, | ||
version: | version: '4.77', | ||
setHeight: 450, | setHeight: 450, | ||
changeTag: 'Cat-a-lot', | |||
settings: { | |||
/* Any category in this category is deemed a disambiguation category; i.e., a category that should not contain | |||
any items, but that contains links to other categories where stuff should be categorized. If you don't have | |||
that concept on your wiki, set it to null. Use blanks, not underscores. */ | |||
disambig_category: 'Disambiguation', // Commons and EnWP | |||
/* Any category in this category is deemed a (soft) redirect to some other category defined by a link | |||
* to another non-blacklisted category. If your wiki doesn't have soft category redirects, set this to null. | |||
* If a soft-redirected category contains more than one link to another non-blacklisted category, it's considered | |||
* a disambiguation category instead. */ | |||
redir_category: 'Category redirects' | |||
}, | |||
init: function () { | init: function () { | ||
$( | // Prevent historical double marker (maybe remove in future) | ||
if ( /Cat-?a-?lot/i.test( msgs[ 'cat-a-lot-pref-save-summary' ] ) ) { mw.messages.set( { 'cat-a-lot-prefix-summary': '', 'cat-a-lot-using-summary': '' } ); } else { | |||
mw.messages.set( { | |||
'cat-a-lot-pref-save-summary': msgs[ 'cat-a-lot-prefix-summary' ] + msgs[ 'cat-a-lot-pref-save-summary' ] + msgs[ 'cat-a-lot-using-summary' ] | |||
} ); | |||
} | |||
// TODO: better extern project support for possible change-tag? (needs currently change after init) | |||
if ( project === 'commonswiki' ) { mw.messages.set( { 'cat-a-lot-using-summary': '' } ); } else { // Reset | |||
this.changeTag = ''; | |||
this.settings.redir_category = ''; | |||
} | |||
this._initSettings(); | |||
$body = $( document.body ); | |||
$container = $( '<div>' ) | |||
.attr( 'id', 'cat_a_lot' ) | |||
.appendTo( $body ); | |||
$dataContainer = $( '<div>' ) | |||
.attr( 'id', 'cat_a_lot_data' ) | |||
.appendTo( $container ); | |||
$searchInputContainer = $( '<div>' ) | |||
.appendTo( $dataContainer ); | |||
$searchInput = $( '<input>', { | |||
id: 'cat_a_lot_searchcatname', | |||
placeholder: msg( 'enter-name' ), | |||
type: 'text' | |||
} ) | |||
.appendTo( $searchInputContainer ); | |||
$resultList = $( '<div>' ) | |||
.attr( 'id', 'cat_a_lot_category_list' ) | |||
.appendTo( $dataContainer ); | |||
$markCounter = $( '<div>' ) | |||
.attr( 'id', 'cat_a_lot_mark_counter' ) | |||
.appendTo( $dataContainer ); | |||
$selections = $( '<div>' ) | |||
.attr( 'id', 'cat_a_lot_selections' ) | |||
.text( msg( 'select' ) + ':' ) | |||
.appendTo( $dataContainer ); | |||
$settingsWrapper = $( '<div>' ) | |||
.attr( 'id', 'cat_a_lot_settings' ) | |||
.appendTo( $dataContainer ); | |||
$settingsLink = $( '<a>', { | |||
id: 'cat_a_lot_config_settings', | |||
title: 'Version ' + this.version, | |||
text: msg( 'config-settings' ) | |||
} ) | |||
.appendTo( $settingsWrapper ); | |||
$head = $( '<div>' ) | |||
.attr( 'id', 'cat_a_lot_head' ) | |||
.appendTo( $container ); | |||
$link = $( '<a>' ) | |||
.attr( 'id', 'cat_a_lot_toggle' ) | |||
.text( 'Cat-a-lot' ) | |||
.appendTo( $head ); | |||
$settingsWrapper.append( $( '<a>', { | |||
href: commonsURL + '?title=Special:MyLanguage/Help:Gadget-Cat-a-lot', | |||
target: '_blank', | |||
style: 'float:right', | |||
title: ( $( '#n-help a' ).attr( 'title' ) || '' ) + ' (v. ' + this.version + ')' | |||
} ).text( '?' ) ); | |||
$container.one( 'mouseover', function () { // Try load on demand earliest as possible | |||
mw.loader.load( [ 'jquery.ui.resizable', 'jquery.ui.draggable' ] ); | |||
} ); | |||
if ( this.origin && !non ) { | |||
$overcat = $( '<a>' ) | |||
.attr( 'id', 'cat_a_lot_overcat' ) | |||
.html( msg( 'overcat' ) ) | |||
.on( 'click', function ( e ) { | |||
CAL.getOverCat( e ); | |||
} ) | |||
.insertBefore( $selections ); | |||
} | |||
if ( ( mw.util.getParamValue( 'withJS' ) === 'MediaWiki:Gadget-Cat-a-lot.js' && | |||
!mw.util.getParamValue( 'withCSS' ) ) || | |||
mw.loader.getState( 'ext.gadget.Cat-a-lot' ) === 'registered' ) { | |||
mw.loader.load( mw.config.get( 'wgServer' ) + '/w/index.php?title=MediaWiki:Gadget-Cat-a-lot.css&action=raw&ctype=text/css', 'text/css' ); | |||
// importStylesheet( 'MediaWiki:Gadget-Cat-a-lot.css' ); | |||
} | |||
reCat = new RegExp( '^\\s*' + CAL.localizedRegex( 14, 'Category' ) + ':', '' ); | |||
$(' | |||
$searchInput.on( 'keypress', function ( e ) { | |||
if ( e.which === 13 ) { | |||
CAL.updateCats( $.trim( $( this ).val().replace( /[\u200E\u200F\u202A-\u202E]/g, '' ) ) ); | |||
mw.cookie.set( 'catAlot', CAL.currentCategory ); | |||
} | |||
} ) | |||
.on( 'input keyup', function () { | |||
}) | var oldVal = this.value, | ||
newVal = oldVal.replace( reCat, '' ); | |||
if ( newVal !== oldVal ) { this.value = newVal; } | |||
if ( !newVal ) { mw.cookie.set( 'catAlot', null ); } | |||
} ); | |||
function initAutocomplete() { | |||
if ( CAL.autoCompleteIsEnabled ) { return; } | |||
.autocomplete({ | CAL.autoCompleteIsEnabled = true; | ||
source: function(request, response) { | |||
if ( !$searchInput.val() && mw.cookie && mw.cookie.get( 'catAlot' ) ) { $searchInput.val( mw.cookie.get( 'catAlot' ) ); } | |||
$searchInput.autocomplete( { | |||
source: function ( request, response ) { | |||
CAL.doAPICall( { | |||
action: 'opensearch', | |||
search: request.term, | |||
redirects: 'resolve', | |||
namespace: 14 | |||
}, function ( data ) { | |||
if ( data[ 1 ] ) { | |||
response( $( data[ 1 ] ) | |||
.map( function ( index, item ) { | |||
return item.replace( reCat, '' ); | |||
} ) ); | |||
} | } | ||
); | |||
} ); | |||
}, | |||
open: function () { | |||
$( '.ui-autocomplete' ) | |||
.position( { | |||
my: is_rtl ? 'left bottom' : 'right bottom', | |||
at: is_rtl ? 'left top' : 'right top', | |||
of: $searchInput | |||
} ); | |||
}, | }, | ||
appendTo: '#cat_a_lot' | |||
$( | } ); | ||
. | } | ||
$( '<a>' ) | |||
// .attr( 'id', 'cat_a_lot_select_all' ) | |||
of: $(' | .text( msg( 'all' ) ) | ||
}); | .on( 'click', function () { | ||
CAL.toggleAll( true ); | |||
} ) | |||
.appendTo( $selections.append( ' ' ) ); | |||
if ( this.settings.editpages ) { | |||
$selectFiles = $( '<a>' ) | |||
.on( 'click', function () { | |||
CAL.toggleAll( 'files' ); | |||
} ); | |||
$selectPages = $( '<a>' ) | |||
.on( 'click', function () { | |||
CAL.toggleAll( 'pages' ); | |||
} ); | |||
$selections.append( $( '<span>' ).hide().append( [ ' / ', $selectFiles, ' / ', $selectPages ] ) ); | |||
} | |||
$selectNone = $( '<a>' ) | |||
// .attr( 'id', 'cat_a_lot_select_none' ) | |||
.text( msg( 'none' ) ) | |||
.on( 'click', function () { | |||
CAL.toggleAll( false ); | |||
} ); | |||
$selectInvert = $( '<a>' ) | |||
.on( 'click', function () { | |||
CAL.toggleAll( null ); | |||
} ); | |||
$selections.append( [ ' • ', $selectNone, ' • ', $selectInvert, | |||
$( '<div>' ).append( [ | |||
$( '<label>' ) | |||
.attr( { | |||
'for': 'cat_a_lot_comment', | |||
style: 'line-height:1.5em;vertical-align:bottom' | |||
} ) | |||
.text( msg( 'comment-label' ) ), | |||
$( '<input>' ) | |||
.attr( { | |||
id: 'cat_a_lot_comment', | |||
type: 'checkbox' | |||
} ) | |||
] ) | |||
] ); | |||
$link | |||
.on( 'click', function () { | |||
$( this ).toggleClass( 'cat_a_lot_enabled' ); | |||
// Load autocomplete on demand | |||
mw.loader.using( 'jquery.ui.autocomplete', initAutocomplete ); | |||
if ( !CAL.executed ) { | |||
$.when( mw.loader.using( [ | |||
'jquery.ui.resizable', | |||
'jquery.ui.draggable', | |||
'jquery.ui.button', | |||
'mediawiki.api.messages', | |||
'mediawiki.jqueryMsg' | |||
] ), $.ready ) | |||
.then( function () { | |||
return new mw.Api().loadMessagesIfMissing( [ | |||
'Cancel', | |||
'Categorytree-not-found', | |||
// 'Checkuser-all', | |||
// 'Code-field-select', | |||
// 'Export-addcat', | |||
'Filerevert-submit', | |||
'Mobile-frontend-return-to-page', | |||
'Ooui-selectfile-placeholder', | |||
// 'Visualeditor-clipboard-copy', | |||
'Wikieditor-loading', | |||
'Prefs-files', | |||
'Categories', | |||
'Checkbox-invert', | |||
'Centralnotice-remove', // 'Ooui-item-remove' | |||
'Apifeatureusage-warnings' | |||
] ); | |||
} ).then( function () { | |||
CAL.run(); | |||
} ); | |||
} else { CAL.run(); } | |||
} ); | |||
$settingsLink | |||
.on( 'click', CAL.manageSettings ); | |||
this.localCatName = formattedNS[ 14 ] + ':'; | |||
mw.loader.using( 'mediawiki.cookie', function () { // Let catAlot stay open | |||
var val = mw.cookie.get( 'catAlotO' ); | |||
if ( val && Number( val ) === ns ) { $link.click(); } | |||
} | |||
); | |||
}, | |||
getOverCat: function ( e ) { | |||
var files = []; | |||
r = 0; // result counter | |||
if ( e ) { | |||
e.preventDefault(); | |||
this.files = this.getMarkedLabels(); // .toArray() not working | |||
for ( var f = 0; f < this.files.length; f++ ) { files.push( this.files[ f ] ); } | |||
} | |||
if ( !files.length || !( files instanceof Array ) ) { return alert( mw.msg( 'Ooui-selectfile-placeholder' ) ); } | |||
this.files = files; | |||
mw.loader.using( [ 'jquery.spinner' ], function () { | |||
$markCounter.injectSpinner( 'overcat' ); | |||
CAL.getFileCats(); | |||
} ); | |||
}, | |||
getFileCats: function () { | |||
var aLen = this.files.length; | |||
var bLen = this.selectedLabels.length; | |||
var file = this.files[ aLen - 1 ][ 0 ]; | |||
$overcat.text( '…' + aLen + '\/' + bLen ); | |||
if ( file ) { | |||
this.doAPICall( { | |||
prop: 'categories', | |||
titles: file | |||
}, this.checkFileCats | |||
); | |||
} | |||
}, | |||
checkFileCats: function ( data ) { | |||
var cc = 0; // current cat counter; | |||
var file = CAL.files.pop(); | |||
if ( data.query && data.query.pages ) { | |||
$.each( data.query.pages, function ( id, page ) { | |||
if ( page.categories ) { | |||
var target = file[ 1 ].removeClass( 'cat_a_lot_selected' ); | |||
$.each( page.categories, function ( c, cat ) { | |||
var title = cat.title.replace( reCat, '' ), | |||
color = 'orange', | |||
mark = function ( kind ) { // kind of category | |||
// TODO: store data to use this for special remove function | |||
if ( kind === 'sub' ) { color = 'green'; } | |||
var border = '3px dotted '; | |||
if ( $.inArray( title, CAL[ kind + 'Cats' ] ) !== -1 ) { | |||
cc++; | |||
target = target.parents( '.gallerybox' ); | |||
target = target[ 0 ] ? target : file[ 1 ]; | |||
target.css( { | |||
border: border + color | |||
} ).prop( 'title', msg( kind + '-cat' ) + title ); | |||
color = 'red'; | |||
return false; | |||
} | |||
}; | |||
mark( 'sub' ); | |||
return mark( 'parent' ); | |||
} ); | |||
if ( cc ) { r++; } | |||
} | } | ||
} ); | |||
} else { mw.log( 'Api-fail', file, data ); } | |||
if ( CAL.files[ 0 ] ) { return setTimeout( function () { CAL.getFileCats(); }, 100 ); } // Api has bad performance here, so we can get only each file separately | |||
$overcat.text( msg( 'pe_file', r, CAL.selectedLabels.length ) ); | |||
$.removeSpinner( 'overcat' ); | |||
}, | }, | ||
findAllLabels: function () { | |||
this.labels = $('#mw- | findAllLabels: function ( searchmode ) { | ||
// It's possible to allow any kind of pages as well but what happens if you click on "select all" and don't expect it | |||
switch ( searchmode ) { | |||
case 'search': | |||
this.labels = this.labels.add( $( 'table.searchResultImage' ).find( 'tr>td:eq(1)' ) ); | |||
if ( this.settings.editpages ) { this.labels = this.labels.add( 'div.mw-search-result-heading' ); } | |||
this.labels. | |||
break; | |||
case 'category': | |||
this.findAllLabels( 'gallery' ); | |||
this.labels = this.labels.add( $( '#mw-category-media' ).find( 'li[class!="gallerybox"]' ) ); | |||
if ( this.settings.editpages ) { | |||
this.pageLabels = $( '#mw-pages, #mw-subcategories' ).find( 'li' ); | |||
// this.files = this.labels; | |||
this.labels = this.labels.add( this.pageLabels ); | |||
} | |||
break; | |||
case 'contribs': | |||
this.labels = this.labels.add( $( 'ul.mw-contributions-list li' ) ); | |||
// FIXME: Filter if !this.settings.editpages | |||
break; | |||
case 'prefix': | |||
this.labels = this.labels.add( $( 'ul.mw-prefixindex-list li' ) ); | |||
break; | |||
case 'listfiles': | |||
// this.labels = this.labels.add( $( 'table.listfiles>tbody>tr' ).find( 'td:eq(1)' ) ); | |||
this.labels = this.labels.add( $( '.TablePager_col_img_name' ) ); | |||
break; | |||
case 'gallery': | |||
// this.labels = this.labels.add( '.gallerybox' ); // TODO incombatible with GalleryDetails | |||
this.labels = this.labels.add( '.gallerytext' ); | |||
break; | |||
} | |||
}, | |||
getTitleFromLink: function ( $a ) { | |||
try { | |||
return decodeURIComponent( $a.attr( 'href' ) ) | |||
.match( /wiki\/(.+?)(?:#.+)?$/ )[ 1 ].replace( /_/g, ' ' ); | |||
} catch ( ex ) { | |||
return ''; | |||
} | } | ||
}, | }, | ||
/** | |||
* @brief Get title from selected pages | |||
* @return [array] touple of page title and $object | |||
*/ | |||
getMarkedLabels: function () { | getMarkedLabels: function () { | ||
this.selectedLabels = this.labels.filter( '.cat_a_lot_selected:visible' ); | |||
this.selectedLabels = this.labels.filter('.cat_a_lot_selected'); | return this.selectedLabels.map( function () { | ||
this.selectedLabels. | var label = $( this ), file = label.find( 'a[title][class$="title"]' ); | ||
var | file = file.length ? file : label.find( 'a[title]' ); | ||
var title = file.attr( 'title' ) || | |||
}) | CAL.getTitleFromLink( file ) || | ||
CAL.getTitleFromLink( label.find( 'a' ) ) || | |||
CAL.getTitleFromLink( label.parent().find( 'a' ) ); // TODO needs optimization | |||
if ( title.indexOf( formattedNS[ 2 ] + ':' ) ) { return [ [ title, label ] ]; } | |||
} ); | |||
}, | }, | ||
updateSelectionCounter: function () { | updateSelectionCounter: function () { | ||
this.selectedLabels = this.labels.filter('.cat_a_lot_selected'); | this.selectedLabels = this.labels.filter( '.cat_a_lot_selected:visible' ); | ||
$(' | var first = $markCounter.is( ':hidden' ); | ||
$markCounter | |||
.html( msg( 'files-selected', this.selectedLabels.length ) ) | |||
.show(); | |||
if ( first && !$dataContainer.is( ':hidden' ) ) { // Workaround to fix position glitch | |||
first = $markCounter.innerHeight(); | |||
$container | |||
.offset( { top: $container.offset().top - first } ) | |||
.height( $container.height() + first ); | |||
$( window ).on( 'beforeunload', function () { | |||
if ( CAL.labels.filter( '.cat_a_lot_selected:visible' )[ 0 ] ) { return 'You have pages selected!'; } // There is a default message in the browser | |||
} ); | |||
} | |||
}, | }, | ||
makeClickable: function () { | makeClickable: function () { | ||
this. | this.labels = $(); | ||
this. | this.pageLabels = $(); // only for distinct all selections | ||
this.findAllLabels( this.searchmode ); | |||
this.labels.catALotShiftClick( function () { | |||
}); | CAL.updateSelectionCounter(); | ||
} ) | |||
.addClass( 'cat_a_lot_label' ); | |||
}, | }, | ||
toggleAll: function (select) { | toggleAll: function ( select ) { | ||
this.labels.toggleClass('cat_a_lot_selected', select); | if ( typeof select === 'string' && this.pageLabels[ 0 ] ) { | ||
this.pageLabels.toggleClass( 'cat_a_lot_selected', true ); | |||
if ( select === 'files' ) // pages get deselected | |||
{ this.labels.toggleClass( 'cat_a_lot_selected' ); } | |||
} else { | |||
// invert / none / all | |||
this.labels.toggleClass( 'cat_a_lot_selected', select ); | |||
} | |||
this.updateSelectionCounter(); | this.updateSelectionCounter(); | ||
}, | }, | ||
getSubCats: function ( | getSubCats: function () { | ||
var data = { | var data = { | ||
list: 'categorymembers', | list: 'categorymembers', | ||
cmtype: 'subcat', | |||
cmlimit: | cmlimit: this.settings.subcatcount, | ||
cmtitle: | cmtitle: 'Category:' + this.currentCategory | ||
}; | }; | ||
this.doAPICall(data, function (result) { | this.doAPICall( data, function ( result ) { | ||
var cats = result.query.categorymembers; | |||
CAL.subCats = []; | |||
for ( var i = 0; i < cats.length; i++ ) { CAL.subCats.push( cats[ i ].title.replace( /^[^:]+:/, '' ) ); } | |||
CAL.catCounter++; | |||
if ( CAL.catCounter === 2 ) { CAL.showCategoryList(); } | |||
} ); | |||
}); | |||
}, | }, | ||
getParentCats: function () { | getParentCats: function () { | ||
var data = { | var data = { | ||
prop: 'categories', | prop: 'categories', | ||
titles: 'Category:' + this.currentCategory | |||
titles: | |||
}; | }; | ||
this.doAPICall(data, function (result) { | this.doAPICall( data, function ( result ) { | ||
CAL.parentCats = []; | |||
var pages = result.query.pages; | var cats, | ||
if (pages[-1] && pages[-1].missing == '') { | pages = result.query.pages, | ||
table = $( '<table>' ); | |||
if ( pages[ -1 ] && pages[ -1 ].missing === '' ) { | |||
$resultList.html( '<span id="cat_a_lot_no_found">' + mw.msg( 'Categorytree-not-found', this.currentCategory ) + '</span>' ); | |||
document.body.style.cursor = 'auto'; | document.body.style.cursor = 'auto'; | ||
CAL.createCatLinks( '→', [ CAL.currentCategory ], table ); | |||
$resultList.append( table ); | |||
return; | return; | ||
} | } | ||
// there should be only one, but we don't know its ID | // there should be only one, but we don't know its ID | ||
for (var id in pages) { | for ( var id in pages ) { cats = pages[ id ].categories || []; } | ||
for ( var i = 0; i < cats.length; i++ ) { CAL.parentCats.push( cats[ i ].title.replace( /^[^:]+:/, '' ) ); } | |||
for (var i = 0; i < cats.length; i++) { | |||
CAL.catCounter++; | |||
if ( CAL.catCounter === 2 ) { CAL.showCategoryList(); } | |||
} ); | |||
}, | |||
localizedRegex: function ( namespaceNumber, fallback ) { | |||
// Copied from HotCat, thanks Lupo. | |||
var wikiTextBlank = '[\\t _\\xA0\\u1680\\u180E\\u2000-\\u200A\\u2028\\u2029\\u202F\\u205F\\u3000]+'; | |||
var wikiTextBlankRE = new RegExp( wikiTextBlank, 'g' ); | |||
var createRegexStr = function ( name ) { | |||
if ( !name || !name.length ) { return ''; } | |||
var regexName = ''; | |||
for ( var i = 0; i < name.length; i++ ) { | |||
var ii = name[ i ]; | |||
var ll = ii.toLowerCase(); | |||
var ul = ii.toUpperCase(); | |||
regexName += ( ll === ul ) ? ii : '[' + ll + ul + ']'; | |||
} | } | ||
return regexName.replace( /([\\\^\$\.\?\*\+\(\)])/g, '\\$1' ) | |||
.replace( wikiTextBlankRE, wikiTextBlank ); | |||
}; | |||
fallback = fallback.toLowerCase(); | |||
var canonical = formattedNS[ namespaceNumber ].toLowerCase(); | |||
var RegexString = createRegexStr( canonical ); | |||
if ( fallback && canonical !== fallback ) { RegexString += '|' + createRegexStr( fallback ); } | |||
for ( var catName in nsIDs ) { if ( typeof catName === 'string' && catName.toLowerCase() !== canonical && catName.toLowerCase() !== fallback && nsIDs[ catName ] === namespaceNumber ) { RegexString += '|' + createRegexStr( catName ); } } | |||
return ( '(?:' + RegexString + ')' ); | |||
}, | }, | ||
var catname = | regexCatBuilder: function ( category ) { | ||
var catname = this.localizedRegex( 14, 'Category' ); | |||
// Build a regexp string for matching the given category: | // Build a regexp string for matching the given category: | ||
// trim leading/trailing whitespace and underscores | // trim leading/trailing whitespace and underscores | ||
category = category.replace(/^[\s_]+ | category = category.replace( /^[\s_]+|[\s_]+$/g, '' ); | ||
// escape regexp metacharacters (= any ASCII punctuation except _) | // escape regexp metacharacters (= any ASCII punctuation except _) | ||
category = | category = mw.RegExp.escape( category ); | ||
// any sequence of spaces and underscores should match any other | // any sequence of spaces and underscores should match any other | ||
category = category.replace(/[\s_]+/g, '[\\s_]+'); | category = category.replace( /[\s_]+/g, '[\\s_]+' ); | ||
// Make the first character case-insensitive: | // Make the first character case-insensitive: | ||
var first = category.substr(0, 1); | var first = category.substr( 0, 1 ); | ||
if (first.toUpperCase() != first.toLowerCase()) category = '[' + first.toUpperCase() + first.toLowerCase() + ']' + category.substr(1); | if ( first.toUpperCase() !== first.toLowerCase() ) { category = '[' + first.toUpperCase() + first.toLowerCase() + ']' + category.substr( 1 ); } | ||
// Compile it into a RegExp that matches MediaWiki category syntax (yeah, it looks ugly): | // Compile it into a RegExp that matches MediaWiki category syntax (yeah, it looks ugly): | ||
// XXX: the first capturing parens are assumed to match the sortkey, if present, including the | but excluding the ]] | // XXX: the first capturing parens are assumed to match the sortkey, if present, including the | but excluding the ]] | ||
return new RegExp('\\[\\[[\\s_]*' + catname + '[\\s_]*:[\\s_]*' + category + '[\\s_]*(\\|[^\\]]*(?:\\][^\\]]+)*)?\\]\\]', ' | return new RegExp( '\\[\\[[\\s_]*' + catname + '[\\s_]*:[\\s_]*' + category + '[\\s_]*(\\|[^\\]]*(?:\\][^\\]]+)*)?\\]\\]\\s*', 'g' ); | ||
}, | |||
getContent: function ( page, targetcat, mode ) { | |||
if ( !this.cancelled ) { | |||
this.doAPICall( { | |||
curtimestamp: 1, | |||
// meta: 'tokens', | |||
prop: 'revisions', | |||
rvprop: 'content|timestamp', | |||
titles: page[ 0 ] | |||
}, function ( result ) { | |||
CAL.editCategories( result, page, targetcat, mode ); | |||
} ); | |||
} | |||
}, | }, | ||
getTargetCat: function ( pages, targetcat, mode ) { | |||
if ( !this.cancelled ) { | |||
this.doAPICall( { | |||
meta: 'tokens', | |||
prop: 'categories|categoryinfo', | |||
titles: 'Category:' + targetcat | |||
}, function ( result ) { | |||
if ( !result || !result.query ) { return; } | |||
CAL.edittoken = result.query.tokens.csrftoken; | |||
result = CAL._getPageQuery( result ); | |||
CAL.checkTargetCat( result ); | |||
for ( var i = 0; i < pages.length; i++ ) { CAL.getContent( pages[ i ], targetcat, mode ); } | |||
} ); | |||
} | |||
}, | |||
checkTargetCat: function ( page ) { | |||
var is_dab = false; // disambiguation | |||
var is_redir = typeof page.redirect === 'string'; // Hard redirect? | |||
if ( typeof page.missing === 'string' ) { return alert( mw.msg( 'Apifeatureusage-warnings', mw.msg( 'Categorytree-not-found', page.title ) ) ); } | |||
var cats = page.categories; | |||
this.is_hidden = page.categoryinfo && typeof page.categoryinfo.hidden === 'string'; | |||
if ( !is_redir && cats && ( CAL.disambig_category || CAL.redir_category ) ) { | |||
for ( var c = 0; c < cats.length; c++ ) { | |||
var cat = cats[ c ].title; | |||
if ( cat ) { // Strip namespace prefix | |||
cat = cat.substring( cat.indexOf( ':' ) + 1 ).replace( /_/g, ' ' ); | |||
if ( cat === CAL.disambig_category ) { | |||
is_dab = true; break; | |||
} else if ( cat === CAL.redir_category ) { | |||
is_redir = true; break; | |||
} | |||
} | |||
} | |||
} | |||
if ( !is_redir && !is_dab ) { return; } | |||
alert( mw.msg( 'Apifeatureusage-warnings', page.title + ' is a ' + CAL.disambig_category ) ); | |||
}, | |||
// Remove {{Uncategorized}} (also with comment). No need to replace it with anything. | |||
removeUncat: function ( text ) { | |||
return ( this.settings.uncat ? text.replace( /\{\{\s*[Uu]ncategorized\s*[^}]*\}\}\s*(<!--.*?-->\s*)?/, '' ) : text ); | |||
}, | }, | ||
doCleanup: function ( text ) { | |||
return ( this.settings.docleanup ? text.replace( /\{\{\s*[Cc]heck categories\s*(\|?.*?)\}\}/, '' ) : text ); | |||
}, | |||
if (result | editCategories: function ( result, file, targetcat, mode ) { | ||
if ( !result || !result.query ) { | |||
this.connectionError.push(file[0]); | // Happens on unstable wifi connections.. | ||
this.connectionError.push( file[ 0 ] ); | |||
this.updateCounter(); | this.updateCounter(); | ||
return; | return; | ||
} | } | ||
var | var otext, | ||
timestamp, | |||
page = CAL._getPageQuery( result ); | |||
if ( page.ns === 2 ) { return; } | |||
var id = page.revisions[ 0 ], | |||
catNS = this.localCatName; // canonical cat-name | |||
this.starttimestamp = result.curtimestamp; | |||
otext = id[ '*' ]; | |||
timestamp = id.timestamp; | |||
var sourcecat = this.origin; | |||
// Check if that file is already in that category | // Check if that file is already in that category | ||
if (mode != | if ( mode !== 'remove' && this.regexCatBuilder( targetcat ).test( otext ) ) { | ||
//If the new cat is already there, just remove the old one | // If the new cat is already there, just remove the old one | ||
if (mode == 'move') { | if ( mode === 'move' ) { | ||
mode='remove'; | mode = 'remove'; | ||
targetcat = sourcecat; | |||
} else { | } else { | ||
this.alreadyThere.push(file[0]); | this.alreadyThere.push( file[ 0 ] ); | ||
this.updateCounter(); | this.updateCounter(); | ||
return; | return; | ||
خط ۲۳۸: | خط ۷۲۴: | ||
} | } | ||
var | // Text modification (following 3 functions are partialy taken from HotCat) | ||
var comment; | var wikiTextBlankOrBidi = '[\\t _\\xA0\\u1680\\u180E\\u2000-\\u200B\\u200E\\u200F\\u2028-\\u202F\\u205F\\u3000]*'; | ||
// Whitespace regexp for handling whitespace between link components. Including the horizontal tab, but not \n\r\f\v: | |||
// a link must be on one single line. | |||
// MediaWiki also removes Unicode bidi override characters in page titles (and namespace names) completely. | |||
// This is *not* handled, as it would require us to allow any of [\u200E\u200F\u202A-\u202E] between any two | |||
// characters inside a category link. It _could_ be done though... We _do_ handle strange spaces, including the | |||
// zero-width space \u200B, and bidi overrides between the components of a category link (adjacent to the colon, | |||
// or adjacent to and inside of "[[" and "]]"). | |||
var findCatsRE = new RegExp( '\\[\\[' + wikiTextBlankOrBidi + this.localizedRegex( 14, 'Category' ) + wikiTextBlankOrBidi + ':[^\\]]+\\]\\]', 'g' ); | |||
function replaceByBlanks( match ) { | |||
return match.replace( /(\s|\S)/g, ' ' ); // /./ doesn't match linebreaks. /(\s|\S)/ does. | |||
} | |||
function find_insertionpoint( wikitext ) { | |||
var copiedtext = wikitext | |||
.replace( /<!--(\s|\S)*?-->/g, replaceByBlanks ) | |||
.replace( /<nowiki>(\s|\S)*?<\/nowiki>/g, replaceByBlanks ); | |||
// Search in copiedtext to avoid that we insert inside an HTML comment or a nowiki "element". | |||
var index = -1; | |||
findCatsRE.lastIndex = 0; | |||
while ( findCatsRE.exec( copiedtext ) !== null ) { index = findCatsRE.lastIndex; } | |||
return index; | |||
} | |||
/** | |||
* @brief Adds the new Category by searching the right insert point, | |||
* if there is text after the category section | |||
* @param [string] wikitext | |||
* @param [string] toAdd | |||
* @return Return wikitext | |||
*/ | |||
function addCategory( wikitext, toAdd ) { | |||
if ( toAdd && toAdd[ 0 ] ) { | |||
// TODO: support sort key | |||
var cat_point = find_insertionpoint( wikitext ); // Position of last category | |||
var newcatstring = '[[' + catNS + toAdd + ']]'; | |||
if ( cat_point > -1 ) { | |||
var suffix = wikitext.substring( cat_point ); | |||
wikitext = wikitext.substring( 0, cat_point ) + ( cat_point ? '\n' : '' ) + newcatstring; | |||
if ( suffix[ 0 ] && suffix.substr( 0, 1 ) !== '\n' ) { wikitext += '\n'; } | |||
wikitext += suffix; | |||
} else { | |||
if ( wikitext[ 0 ] && wikitext.substr( wikitext.length - 1, 1 ) !== '\n' ) { wikitext += '\n'; } | |||
wikitext += ( wikitext[ 0 ] ? '\n' : '' ) + newcatstring; | |||
} | |||
} | |||
return wikitext; | |||
} | |||
// End HotCat functions | |||
var text = otext, | |||
arr = is_rtl ? '\u2190' : '\u2192', // left and right arrows. Don't use ← and → in the code. | |||
sumCmt, // summary comment | |||
sumCmtShort; | |||
// Fix text | // Fix text | ||
switch (mode) { | switch ( mode ) { | ||
case 'add': | |||
text = addCategory( text, targetcat ); | |||
sumCmt = msg( 'summary-add' ).replace( /\$1/g, targetcat ); | |||
sumCmtShort = '+[[' + catNS + targetcat + ']]'; | |||
break; | |||
case 'copy': | |||
text = text.replace( this.regexCatBuilder( sourcecat ), '[[' + catNS + sourcecat + '$1]]\n[[' + catNS + targetcat + '$1]]\n' ); | |||
sumCmt = msg( 'summary-copy' ).replace( /\$1/g, sourcecat ).replace( /\$2/g, targetcat ); | |||
sumCmtShort = '+[[' + catNS + sourcecat + ']]' + arr + '[[' + catNS + targetcat + ']]'; | |||
// If category is added through template: | |||
if ( otext === text ) { text = addCategory( text, targetcat ); } | |||
break; | |||
case 'move': | |||
text = text.replace( this.regexCatBuilder( sourcecat ), '[[' + catNS + targetcat + '$1]]\n' ); | |||
sumCmt = msg( 'summary-move' ).replace( /\$1/g, sourcecat ).replace( /\$2/g, targetcat ); | |||
sumCmtShort = '±[[' + catNS + sourcecat + ']]' + arr + '[[' + catNS + targetcat + ']]'; | |||
break; | |||
case 'remove': | |||
text = text.replace( this.regexCatBuilder( targetcat ), '' ); | |||
sumCmt = msg( 'summary-remove' ).replace( /\$1/g, targetcat ); | |||
sumCmtShort = '-[[' + catNS + targetcat + ']]'; | |||
break; | |||
} | } | ||
if (text == otext) { | if ( text === otext ) { | ||
this.notFound.push(file[0]); | this.notFound.push( file[ 0 ] ); | ||
this.updateCounter(); | this.updateCounter(); | ||
return; | return; | ||
} | |||
otext = text; | |||
// Remove {{uncat}} after we checked whether we changed the text successfully. | |||
// Otherwise we might fail to do the changes, but still replace {{uncat}} | |||
if ( mode !== 'remove' && ( !non || userGrp.indexOf( 'autoconfirmed' ) > -1 ) ) { | |||
if ( !this.is_hidden ) { | |||
text = this.removeUncat( text ); | |||
if ( text.length !== otext.length ) { sumCmt += '; ' + msg( 'uncatpref' ); } | |||
} | |||
text = this.doCleanup( text ); | |||
} | |||
sumCmt += this.summary ? ' ' + this.summary : ''; | |||
var preM = msg( 'prefix-summary' ); | |||
var usgM = msg( 'using-summary' ); | |||
// Try shorten summary | |||
if ( preM || usgM ) { | |||
sumCmt = ( sumCmt.length > 250 - preM.length - usgM.length ) ? | |||
sumCmt + ' (CatAlot)' : preM + sumCmt + usgM; | |||
} | } | ||
if ( sumCmt.length > 254 ) // Try short summary | |||
{ sumCmt = sumCmtShort; } | |||
var data = { | var data = { | ||
action: 'edit', | action: 'edit', | ||
summary: | assert: 'user', | ||
title: file[0], | summary: sumCmt, | ||
title: file[ 0 ], | |||
starttimestamp: starttimestamp, | text: text, | ||
bot: true, | |||
starttimestamp: this.starttimestamp, | |||
basetimestamp: timestamp, | basetimestamp: timestamp, | ||
watchlist: this.settings.watchlist, | |||
minor: this.settings.minor, | |||
tags: this.changeTag, | |||
token: this.edittoken | |||
}; | }; | ||
this.doAPICall( data, function ( r ) { | |||
delete CAL.XHR[ file[ 0 ] ]; | |||
return CAL.updateUndoCounter( r ); | |||
} ); | |||
this.markAsDone( file[ 1 ], mode, targetcat ); | |||
}, | |||
markAsDone: function ( label, mode, targetcat ) { | |||
mode = ( function ( m ) { | |||
}); | switch ( m ) { | ||
case 'add': return 'added-cat'; | |||
case 'copy': return 'copied-cat'; | |||
case 'move': return 'moved-cat'; | |||
case 'remove': return 'removed-cat'; | |||
} | |||
}( mode ) ); | |||
label.addClass( 'cat_a_lot_markAsDone' ).append( '<br>' + msg( mode, targetcat ) ); | |||
}, | }, | ||
updateUndoCounter: function ( r ) { | |||
this.updateCounter(); | |||
if ( !r.edit || r.edit.result !== 'Success' ) { return; } | |||
r = r.edit; | |||
this.undoList.push( { | |||
title: r.title, | |||
id: r.newrevid, | |||
timestamp: r.newtimestamp | |||
} ); | |||
} | |||
}, | }, | ||
updateCounter: function () { | updateCounter: function () { | ||
this.counterCurrent++; | this.counterCurrent++; | ||
if (this.counterCurrent > this.counterNeeded) this.displayResult(); | if ( this.counterCurrent > this.counterNeeded ) { this.displayResult(); } else { this.domCounter.text( this.counterCurrent ); } | ||
}, | }, | ||
displayResult: function () { | displayResult: function () { | ||
document.body.style.cursor = 'auto'; | document.body.style.cursor = 'auto'; | ||
$(' | $.removeSpinner( 'fb-dialog' ); | ||
this.progressDialog.parent() | |||
var rep = this.domCounter.parent() | .addClass( 'cat_a_lot_done' ) | ||
.find( '.ui-dialog-buttonpane button span' ).eq( 0 ) | |||
rep.append( this. | .text( mw.msg( 'Mobile-frontend-return-to-page' ) ); | ||
var rep = this.domCounter.parent() | |||
.height( 'auto' ) | |||
.html( '<h3>' + msg( 'done' ) + '</h3>' ) | |||
.append( msg( 'all-done' ) + '<br>' ); | |||
if ( this.alreadyThere.length ) { | |||
rep.append( '<h5>' + msg( 'skipped-already', this.alreadyThere.length ) + '</h5>' ) | |||
.append( this.alreadyThere.join( '<br>' ) ); | |||
} | |||
if ( this.notFound.length ) { | |||
rep.append( '<h5>' + msg( 'skipped-not-found', this.notFound.length ) + '</h5>' ) | |||
.append( this.notFound.join( '<br>' ) ); | |||
} | } | ||
if (this. | |||
rep.append( this. | if ( this.connectionError.length ) { | ||
rep.append( '<h5>' + msg( 'skipped-server', this.connectionError.length ) + '</h5>' ) | |||
.append( this.connectionError.join( '<br>' ) ); | |||
} | } | ||
}, | }, | ||
/** | |||
this. | * @brief set parameters for API call, | ||
* convert targetcat to string, get selected pages/files | |||
* @param [dom object] targetcat with data | |||
* @param [string] mode action | |||
* @return Return API call getTargetCat with pages | |||
*/ | |||
doSomething: function ( targetcat, mode ) { | |||
var pages = this.getMarkedLabels(); | |||
if ( !pages.length ) { return alert( mw.msg( 'Ooui-selectfile-placeholder' ) ); } | |||
targetcat = $( targetcat ).closest( 'tr' ).data( 'cat' ); | |||
this.notFound = []; | this.notFound = []; | ||
this.alreadyThere = []; | this.alreadyThere = []; | ||
this.connectionError = []; | this.connectionError = []; | ||
this.counterCurrent = 1; | this.counterCurrent = 1; | ||
this.counterNeeded = | this.counterNeeded = pages.length; | ||
this. | this.undoList = []; | ||
this.XHR = {}; | |||
this.cancelled = 0; | |||
this.summary = ''; | |||
if ( $( '#cat_a_lot_comment' ).prop( 'checked' ) ) { this.summary = window.prompt( msg( 'edit-question' ), '' ); } // TODO custom pre-value | |||
if ( this.summary !== null ) { | |||
mw.loader.using( [ 'jquery.ui.dialog', 'jquery.spinner', 'mediawiki.RegExp' ], function () { | |||
CAL.showProgress(); | |||
CAL.getTargetCat( pages, targetcat, mode ); | |||
} ); | |||
} | } | ||
}, | }, | ||
doAPICall: function (params, callback) { | doAPICall: function ( params, callback ) { | ||
params = $.extend( { | |||
action: 'query', | |||
format: 'json' | |||
}, params ); | |||
var i = 0, | |||
apiUrl = this.apiUrl, | |||
doCall, | |||
handleError = function ( jqXHR, textStatus, errorThrown ) { | |||
mw.log( 'Error: ', jqXHR, textStatus, errorThrown ); | |||
if ( i < 4 ) { | |||
window.setTimeout( doCall, 300 ); | |||
i++; | |||
} else if ( params.title ) { | |||
this.connectionError.push( params.title ); | |||
this.updateCounter(); | |||
return; | |||
} | |||
}; | |||
doCall = function () { | |||
var xhr = $.ajax( { | |||
url: apiUrl, | |||
cache: false, | |||
dataType: 'json', | |||
data: params, | |||
type: 'POST', | |||
success: callback, | |||
error: handleError | |||
} ); | |||
if ( params.action === 'edit' && !CAL.cancelled ) { CAL.XHR[ params.title ] = xhr; } | |||
}; | |||
doCall(); | |||
}, | }, | ||
createCatLinks: function (symbol, list) { | createCatLinks: function ( symbol, list, table ) { | ||
list.sort(); | list.sort(); | ||
var | var button = ( this.settings.button && mw.loader.getState( 'jquery.ui.button' ) === 'ready' ) ? 1 : 0; | ||
for (var | for ( var c = 0; c < list.length; c++ ) { | ||
var | var $tr = $( '<tr>' ), | ||
$link = $( '<a>', { | |||
href: mw.util.getUrl( CAL.localCatName + list[ c ] ), | |||
text: list[ c ] | |||
} ), | |||
$buttons = []; | |||
$tr.data( 'cat', list[ c ] ); | |||
$link.on( 'click', function ( e ) { | |||
if ( !e.ctrlKey ) { | |||
e.preventDefault(); | |||
CAL.updateCats( $( this ).closest( 'tr' ).data( 'cat' ) ); | |||
} | |||
} ); | |||
$tr.append( $( '<td>' ).text( symbol ) ) | |||
.append( $( '<td>' ).append( $link ) ); | |||
$buttons.push( $( '<a>' ) | |||
.text( mw.msg( 'Centralnotice-remove' ) ) | |||
.on( 'click', function () { | |||
CAL.doSomething( this, 'remove' ); | |||
} ) | |||
.addClass( 'cat_a_lot_move' ) | |||
); | |||
if ( button ) { | |||
$buttons.slice( -1 )[ 0 ].button( { | |||
icons: { primary: 'ui-icon-minusthick' }, | |||
showLabel: false, | |||
text: false | |||
} ); | |||
} | |||
if ( this.origin ) { | |||
// Can't move to source category | |||
if ( list[ c ] !== this.origin ) { | |||
$buttons.push( $( '<a>' ) | |||
.text( msg( 'move' ) ) | |||
.on( 'click', function () { | |||
CAL.doSomething( this, 'move' ); | |||
} ) | |||
.addClass( 'cat_a_lot_move' ) | |||
); | |||
if ( button ) { | |||
$buttons.slice( -1 )[ 0 ].button( { | |||
icons: { primary: 'ui-icon-arrowthick-1-e' }, | |||
showLabel: false, | |||
text: false | |||
} ); | |||
} | |||
$buttons.push( $( '<a>' ) | |||
.text( msg( 'copy' ) ) | |||
.on( 'click', function () { | |||
CAL.doSomething( this, 'copy' ); | |||
} | } ) | ||
.addClass( 'cat_a_lot_action' ) | |||
); | |||
if ( button ) { | |||
$buttons.slice( -1 )[ 0 ].button( { | |||
icons: { primary: 'ui-icon-plusthick' }, | |||
showLabel: false, | |||
text: false | |||
} ); | |||
} | |||
} | |||
} else { | } else { | ||
$buttons.push( $( '<a>' ) | |||
.text( msg( 'add' ) ) | |||
.on( 'click', function () { | |||
} | CAL.doSomething( this, 'add' ); | ||
} ) | |||
.addClass( 'cat_a_lot_action' ) | |||
); | |||
if ( button ) { | |||
$buttons.slice( -1 )[ 0 ].button( { | |||
icons: { primary: 'ui-icon-plusthick' }, | |||
showLabel: false, | |||
text: false | |||
} ); | |||
} | |||
} | } | ||
// TODO CSS may extern | |||
var css = button ? { fontSize: '.6em', margin: '0', width: '2.5em' } : {}; | |||
for ( var b = 0; b < $buttons.length; b++ ) { $tr.append( $( '<td>' ).append( $buttons[ b ].css( css ) ) ); } | |||
table.append( $tr ); | |||
} | } | ||
}, | }, | ||
خط ۴۳۴: | خط ۱٬۰۸۵: | ||
this.getParentCats(); | this.getParentCats(); | ||
this.getSubCats(); | this.getSubCats(); | ||
}, | |||
_getPageQuery: function ( data ) { | |||
// There should be only one, but we don't know its ID | |||
if ( data && data.query && data.query.pages ) { | |||
data = data.query.pages; | |||
for ( var p in data ) { return data[ p ]; } | |||
} | |||
}, | |||
/** | |||
* @brief takes this.currentCategory if redir_category is configured | |||
** Cat pages with more than one cat link are still not supported for sure | |||
* @return soft redirected cat | |||
*/ | |||
solveSoftRedirect: function () { | |||
this.doAPICall( { | |||
prop: 'links', // TODO: For more accuracy the revisions could be checked | |||
titles: 'Category:' + this.currentCategory, | |||
// 'rvprop': 'content', | |||
// 'pllimit': 'max', | |||
plnamespace: 14 | |||
}, function ( page ) { | |||
page = CAL._getPageQuery( page ); | |||
if ( page ) { | |||
var lks = page.links; | |||
if ( lks && lks.length === 1 && lks[ 0 ].title ) { | |||
CAL.currentCategory = lks[ 0 ].title.replace( reCat, '' ); | |||
$searchInput.val( CAL.currentCategory ); | |||
return CAL.getCategoryList(); | |||
} else { | |||
// TODO? better translatable warning message: "Please solve the category soft redirect manually!" | |||
$resultList.html( '<span id="cat_a_lot_no_found">' + mw.msg( 'Apifeatureusage-warnings', mw.msg( 'Categorytree-not-found', CAL.currentCategory ) ) + '</span>' ); | |||
} | |||
} | |||
} ); | |||
}, | }, | ||
showCategoryList: function () { | showCategoryList: function () { | ||
if ( this.settings.redir_category && this.settings.redir_category === this.parentCats[ 0 ] ) { return this.solveSoftRedirect(); } | |||
var table = $( '<table>' ); | |||
this.createCatLinks( | this.createCatLinks( '↑', this.parentCats, table ); | ||
this.createCatLinks( | this.createCatLinks( '→', [ this.currentCategory ], table ); | ||
this.createCatLinks( | // Show on soft-redirect | ||
if ( $searchInput.val() === this.currentCategory && this.origin !== this.currentCategory ) { this.createCatLinks( '→', [ this.origin ], table ); } | |||
this.createCatLinks( '↓', this.subCats, table ); | |||
$resultList.empty(); | |||
$resultList.append( table ); | |||
document.body.style.cursor = 'auto'; | document.body.style.cursor = 'auto'; | ||
//Reset width | |||
// Reset width | |||
$container.width( '' ); | |||
$container.height( '' ); | |||
$container.width( Math.min( table.width() * 1.1 + 15, $( window ).width() - 10 ) ); | |||
$resultList.css( { | |||
maxHeight: Math.min( this.setHeight, $( window ).height() - $container.position().top - $settingsLink.outerHeight() - $selections.outerHeight() - 15 ), | |||
height: '' | |||
} ); | |||
table.width( '100%' ); | |||
$container.height( Math.min( $container.height(), $head.offset().top - $container.offset().top + 10 ) ); | |||
$container.offset( { left: $( window ).width() - $container.outerWidth() } ); // Fix overlap | |||
}, | }, | ||
updateCats: function (newcat) { | updateCats: function ( newcat ) { | ||
document.body.style.cursor = 'wait'; | document.body.style.cursor = 'wait'; | ||
this.currentCategory = newcat; | this.currentCategory = newcat; | ||
$resultList.html( '<div class="cat_a_lot_loading">' + mw.msg( 'Wikieditor-loading' ) + '</div>' ); | |||
this.getCategoryList(); | this.getCategoryList(); | ||
}, | }, | ||
doUndo: function () { | |||
this.cancelled = 0; | |||
this.doAbort(); | |||
if ( !this.undoList.length ) { return; } | |||
$( '.cat_a_lot_feedback' ).removeClass( 'cat_a_lot_done' ); | |||
this.counterNeeded = this.undoList.length; | |||
this.counterCurrent = 1; | |||
document.body.style.cursor = 'wait'; | |||
var query = { | |||
action: 'edit', | |||
user: mw.config.get( 'wgUserName' ), | |||
bot: true, | |||
minor: this.settings.minor, | |||
starttimestamp: this.starttimestamp, | |||
watchlist: this.settings.watchlist, | |||
tags: this.changeTag, | |||
token: this.edittoken | |||
}; | |||
for ( var i = 0; i < this.undoList.length; i++ ) { | |||
var uID = this.undoList[ i ]; | |||
query.title = uID.title; | |||
query.undo = uID.id; | |||
query.basetimestamp = uID.timestamp; | |||
this.doAPICall( query, function ( r ) { | |||
// TODO: Add "details" to progressbar? | |||
// $resultList.append( [mw.msg('Filerevert-submit') + " done " + r.edit.title, '<br>' ] ); | |||
if ( r && r.edit ) { mw.log( 'Revert done', r.edit.title ); } | |||
CAL.updateCounter(); | |||
} ); | |||
} | |||
}, | |||
doAbort: function () { | |||
for ( var t in this.XHR ) { this.XHR[ t ].abort(); } | |||
if ( this.cancelled ) { // still not for undo | |||
this.progressDialog.remove(); | |||
this.toggleAll( false ); | |||
$head.last().show(); | |||
} | |||
this.cancelled = 1; | |||
}, | |||
showProgress: function () { | showProgress: function () { | ||
document.body.style.cursor = 'wait'; | document.body.style.cursor = 'wait'; | ||
this.progressDialog = $( '<div>' ) | |||
.html( ' ' + msg( 'editing' ) + ' <span id="cat_a_lot_current">' + CAL.counterCurrent + '</span> ' + msg( 'of' ) + CAL.counterNeeded ) | |||
.prepend( $.createSpinner( { id: 'fb-dialog', size: 'large' } ) ) | |||
.dialog( { | |||
width: 450, | |||
height: 180, | |||
minHeight: 90, | |||
modal: true, | |||
resizable: false, | |||
draggable: false, | |||
// closeOnEscape: true, | |||
dialogClass: 'cat_a_lot_feedback', | |||
buttons: [ { | |||
text: mw.msg( 'Cancel' ), // Stops all actions | |||
click: function () { | |||
$( this ).dialog( 'close' ); | |||
} | |||
} ], | |||
close: function () { | |||
CAL.cancelled = 1; | |||
CAL.doAbort(); | |||
$( this ).remove(); | |||
}, | |||
open: function ( event, ui ) { // Workaround modify | |||
ui = $( this ).parent(); | |||
ui.find( '.ui-dialog-titlebar' ).hide(); | |||
ui.find( '.ui-dialog-buttonpane.ui-widget-content' ) | |||
.removeClass( 'ui-widget-content' ); | |||
/* .find( 'span' ).css( { fontSize: '90%' } )*/ | |||
} | |||
} ); | |||
if ( $head.children().length < 3 ) { | |||
$( '<span>' ) | |||
.css( { | |||
'float': 'right', | |||
fontSize: '75%' | |||
} ) | |||
.append( [ '[ ', | |||
$( '<a>', { title: 'Revert all last done edits' } ) // TODO i18n | |||
.on( 'click', function () { | |||
if ( window.confirm( mw.msg( 'Apifeatureusage-warnings', this.title + '⁉' ) ) ) { | |||
CAL.doUndo(); | |||
$( this ).parent().remove(); | |||
} | |||
return false; | |||
} ) | |||
.addClass( 'new' ) | |||
.text( mw.msg( 'Filerevert-submit' ) ), | |||
' ]' | |||
] ).insertAfter( $link ); | |||
} | |||
this. | this.domCounter = $( '#cat_a_lot_current' ); | ||
. | }, | ||
. | |||
minimize: function ( e ) { | |||
CAL.top = Math.max( 0, $container.position().top ); | |||
CAL.height = $container.height(); | |||
$dataContainer.hide(); | |||
$container.animate( { | |||
height: $head.height(), | |||
top: $( window ).height() - $head.height() * 1.4 | |||
}, function () { | |||
}) | $( e.target ).one( 'click', CAL.maximize ); | ||
} ); | |||
}, | |||
maximize: function ( e ) { | |||
$dataContainer.show(); | |||
$container.animate( { | |||
top: CAL.top, | |||
height: CAL.height | |||
}, function () { | |||
$( e.target ).one( 'click', CAL.minimize ); | |||
} ); | |||
}, | }, | ||
run: function () { | run: function () { | ||
if ($('.cat_a_lot_enabled') | if ( $( '.cat_a_lot_enabled' )[ 0 ] ) { | ||
this.makeClickable(); | this.makeClickable(); | ||
$( | if ( !this.executed ) { // only once | ||
$(' | $selectInvert.text( mw.msg( 'Checkbox-invert' ) ); | ||
if ( this.settings.editpages && this.pageLabels[ 0 ] ) { | |||
$selectFiles.text( mw.msg( 'Prefs-files' ) ); | |||
$selectPages.text( mw.msg( 'Categories' ) ).parent().show(); | |||
} | |||
$link.after( $( '<a>' ) | |||
.text( '–' ) | |||
.css( { fontWeight: 'bold', marginLeft: '.7em' } ) | |||
.one( 'click', this.minimize ) | |||
); | |||
} | |||
$dataContainer.show(); | |||
$container.one( 'mouseover', function () { | |||
$( this ) | |||
.resizable( { | |||
handles: 'n', | |||
alsoResize: '#cat_a_lot_category_list', | |||
resize: function () { | |||
$resultList | |||
.css( { | |||
maxHeight: '', | |||
width: '' | |||
} ); | |||
}, | |||
start: function ( e, ui ) { // Otherwise box get static if sametime resize with draggable | |||
ui.helper.css( { | |||
top: ui.helper.offset().top - $( window ).scrollTop(), | |||
position: 'fixed' | |||
} ); | |||
}, | |||
stop: function () { | |||
CAL.setHeight = $resultList.height(); | |||
} | |||
} ) | |||
.draggable( { | |||
cursor: 'move', | |||
start: function ( e, ui ) { | |||
ui.helper.on( 'click.prevent', | |||
function ( e ) { e.preventDefault(); } | |||
); | |||
ui.helper.css( 'height', ui.helper.height() ); | |||
}, | |||
stop: function ( e, ui ) { | |||
setTimeout( | |||
function () { | |||
ui.helper.off( 'click.prevent' ); | |||
}, 300 | |||
); | |||
} | |||
} ) | |||
.one( 'mousedown', function () { | |||
$container.height( $container.height() ); // Workaround to calculate | |||
} ); | |||
$resultList | |||
.css( { maxHeight: 450 } ); | |||
} ); | |||
this.updateCats( this.origin || 'Images' ); | |||
} else { | $link.html( $( '<span>' ) | ||
$ | .text( '×' ) | ||
$( | .css( { font: 'bold 2em monospace', lineHeight: '.75em' } ) | ||
//Unbind click handlers | ); | ||
this.labels.off('click'); | $link.next().show(); | ||
if ( this.cancelled ) { $head.last().show(); } | |||
mw.cookie.set( 'catAlotO', ns ); // Let stay open on new window | |||
} else { // Reset | |||
$dataContainer.hide(); | |||
$container | |||
.draggable( 'destroy' ) | |||
.resizable( 'destroy' ) | |||
.removeAttr( 'style' ); | |||
// Unbind click handlers | |||
this.labels.off( 'click.catALot' ); | |||
this.setHeight = 450; | |||
$link.text( 'Cat-a-lot' ) | |||
.nextAll().hide(); | |||
this.executed = 1; | |||
mw.cookie.set( 'catAlotO', null ); | |||
} | } | ||
}, | }, | ||
manageSettings: function () { | |||
mw.loader.using( [ 'ext.gadget.SettingsManager', 'ext.gadget.SettingsUI', 'jquery.ui.progressbar' ], CAL._manageSettings ); | |||
}, | |||
_manageSettings: function () { | |||
mw.libs.SettingsUI( CAL.defaults, 'Cat-a-lot' ) | |||
.show() | |||
.done( function ( s, verbose, loc, settingsOut, $dlg ) { | |||
var mustRestart = false, | |||
_restart = function () { | |||
if ( !mustRestart ) { return; } | |||
$container.remove(); | |||
CAL.labels.off( 'click.catALot' ); | |||
CAL.init(); | |||
}, | |||
_saveToJS = function () { | |||
var opt = mw.libs.settingsManager.option( { | |||
optionName: 'catALotPrefs', | |||
value: CAL.settings, | |||
encloseSignature: 'catALot', | |||
encloseBlock: '////////// Cat-a-lot user preferences //////////\n', | |||
triggerSaveAt: /Cat.?A.?Lot/i, | |||
editSummary: msg( 'pref-save-summary' ) | |||
} ), | |||
oldHeight = $dlg.height(), | |||
$prog = $( '<div>' ); | |||
$dlg.css( 'height', oldHeight ) | |||
.html( '' ); | |||
$prog.css( { | |||
height: Math.round( oldHeight / 8 ), | |||
'margin-top': Math.round( ( 7 * oldHeight ) / 16 ) | |||
} ) | |||
.appendTo( $dlg ); | |||
$dlg.parent() | |||
.find( '.ui-dialog-buttonpane button' ) | |||
.button( 'option', 'disabled', true ); | |||
opt.save() | |||
.done( function ( text, progress ) { | |||
$prog.progressbar( { | |||
value: progress | |||
} ); | |||
$prog.fadeOut( function () { | |||
$dlg.dialog( 'close' ); | |||
_restart(); | |||
} ); | |||
} ) | |||
.progress( function ( text, progress ) { | |||
$prog.progressbar( { | |||
value: progress | |||
} ); | |||
// TODO: Add "details" to progressbar | |||
} ) | |||
.fail( function ( text ) { | |||
$prog.addClass( 'ui-state-error' ); | |||
$dlg.prepend( $( '<p>' ) | |||
.text( text ) ); | |||
} ); | |||
}; | |||
$.each( settingsOut, function ( n, v ) { | |||
if ( v.forcerestart && CAL.settings[ v.name ] !== v.value ) { mustRestart = true; } | |||
CAL.settings[ v.name ] = CAL.catALotPrefs[ v.name ] = v.value; | |||
} ); | |||
switch ( loc ) { | |||
case 'page': | |||
$dlg.dialog( 'close' ); | |||
_restart(); | |||
break; | |||
case 'account-publicly': | |||
_saveToJS(); | |||
break; | |||
} | |||
} ); | |||
}, | |||
_initSettings: function () { | |||
if ( this.settings.watchlist ) { return; } | |||
this.catALotPrefs = window.catALotPrefs || {}; | |||
for ( var i = 0; i < this.defaults.length; i++ ) { | |||
var v = this.defaults[ i ]; | |||
v.value = this.settings[ v.name ] = ( this.catALotPrefs[ v.name ] || v['default'] ); | |||
}: | v.label = msg( v.label_i18n ); | ||
if ( v.select_i18n ) { | |||
v.select = {}; | |||
$.each( v.select_i18n, function ( i18nk, val ) { | |||
v.select[ msg( i18nk ) ] = val; | |||
} ); | |||
} | |||
} | |||
}, | |||
/* eslint-disable camelcase */ | |||
defaults: [ { | |||
name: 'watchlist', | |||
'default': 'preferences', | |||
label_i18n: 'watchlistpref', | |||
select_i18n: { | |||
watch_pref: 'preferences', | |||
watch_nochange: 'nochange', | |||
watch_watch: 'watch', | |||
watch_unwatch: 'unwatch' | |||
} | |||
}, { | |||
name: 'minor', | |||
'default': false, | |||
label_i18n: 'minorpref' | |||
}, { | |||
name: 'editpages', | |||
'default': project !== 'commonswiki', // on Commons false | |||
label_i18n: 'editpagespref', | |||
forcerestart: true | |||
}, { | |||
name: 'docleanup', | |||
'default': false, | |||
label_i18n: 'docleanuppref' | |||
}, { | |||
name: 'subcatcount', | |||
'default': 50, | |||
min: 5, | |||
max: 500, | |||
label_i18n: 'subcatcountpref', | |||
forcerestart: true | |||
}, { | |||
name: 'uncat', | |||
'default': project === 'commonswiki', // on Commons true | |||
label_i18n: 'uncatpref' | |||
}, { | |||
name: 'button', | |||
'default': true, | |||
label_i18n: 'buttonpref' | |||
} ] | |||
/* eslint-enable camelcase */ | |||
}; | |||
// The gadget is not immediately needed, so let the page load normally | |||
window.setTimeout( function () { | |||
non = mw.config.get( 'wgUserName' ); | |||
if ( non ) { | |||
if ( mw.config.get( 'wgRelevantUserName' ) === non ) { non = 0; } else { | |||
$.each( [ 'sysop', 'filemover', 'editor', 'rollbacker', 'patroller', 'autopatrolled', 'image-reviewer', 'reviewer', 'extendedconfirmed' ], function ( i, v ) { | |||
non = $.inArray( v, userGrp ) === -1; | |||
return non; | |||
} ); | |||
} | |||
} else { non = 1; } | |||
// | switch ( ns ) { | ||
case 14: | |||
CAL.searchmode = 'category'; | |||
CAL.origin = mw.config.get( 'wgTitle' ); | |||
break; | |||
case -1: | |||
CAL.searchmode = { | |||
// list of accepted special page names mapped to search mode names | |||
Contributions: 'contribs', | |||
Listfiles: non ? null : 'listfiles', | |||
Prefixindex: non ? null : 'prefix', | |||
Search: 'search', | |||
Uncategorizedimages: 'gallery' | |||
}[ mw.config.get( 'wgCanonicalSpecialPageName' ) ]; | |||
break; | |||
case 2: | |||
case 0: | |||
CAL.searchmode = 'gallery'; | |||
var parents = $( '#mw-normal-catlinks ul' ).find( 'a[title]' ), n; | |||
parents.each( function ( i ) { | |||
if ( new RegExp( mw.config.get( 'wgTitle' ), 'i' ).test( $( this ).text() ) ) { | |||
n = i; | |||
return false; | |||
} | |||
} ); | |||
CAL.origin = parents.eq( n || 0 ).text(); | |||
} | |||
if ( CAL.searchmode ) { | |||
var loadingLocalizations = 1; | |||
var loadLocalization = function ( lang, cb ) { | |||
loadingLocalizations++; | |||
switch ( lang ) { | |||
case 'zh-hk': | |||
case 'zh-mo': | |||
case 'zh-tw': | |||
lang = 'zh-hant'; | |||
break; | |||
case 'zh': | |||
case 'zh-cn': | |||
case 'zh-my': | |||
case 'zh-sg': | |||
lang = 'zh-hans'; | |||
break; | |||
} | |||
$.ajax( { | |||
url: commonsURL, | |||
dataType: 'script', | |||
data: { | |||
title: 'MediaWiki:Gadget-Cat-a-lot.js/' + lang, | |||
action: 'raw', | |||
ctype: 'text/javascript', | |||
// Allow caching for 28 days | |||
maxage: 2419200, | |||
smaxage: 2419200 | |||
}, | |||
cache: true, | |||
success: cb, | |||
error: cb | |||
} ); | |||
}; | |||
var maybeLaunch = function () { | |||
loadingLocalizations--; | |||
function init() { | |||
$( function () { | |||
CAL.init(); | |||
} ); | |||
} | |||
if ( !loadingLocalizations ) { mw.loader.using( [ 'user' ], init, init ); } | |||
}; | |||
var userlang = mw.config.get( 'wgUserLanguage' ), | |||
contlang = mw.config.get( 'wgContentLanguage' ); | |||
if ( userlang !== 'en' ) { loadLocalization( userlang, maybeLaunch ); } | |||
if ( $.inArray( contlang, [ 'en', userlang ] ) === -1 ) { loadLocalization( contlang, maybeLaunch ); } | |||
maybeLaunch(); | |||
} | } | ||
}; | }, 400 ); | ||
/** | |||
* Derivative work of | |||
* (replace "checkboxes" with cat-a-lot labels in your mind) | |||
** | |||
* jQuery checkboxShiftClick | |||
* | |||
* This will enable checkboxes to be checked or unchecked in a row by clicking one, holding shift and clicking another one | |||
* | |||
* @author Krinkle <krinklemail@gmail.com> | |||
* @license GPL v2 | |||
*/ | |||
$.fn.catALotShiftClick = function ( cb ) { | |||
var prevCheckbox = null, | |||
$box = this; | |||
// When our boxes are clicked.. | |||
$box.on( 'click.catALot', function ( e ) { | |||
// Prevent following the link and text selection | |||
if ( !e.ctrlKey ) { e.preventDefault(); } | |||
// Highlight last selected | |||
$( '#cat_a_lot_last_selected' ) | |||
.removeAttr( 'id' ); | |||
var $thisControl = $( e.target ), | |||
method; | |||
if ( !$thisControl.hasClass( 'cat_a_lot_label' ) ) { $thisControl = $thisControl.parents( '.cat_a_lot_label' ); } | |||
$thisControl.attr( 'id', 'cat_a_lot_last_selected' ) | |||
.toggleClass( 'cat_a_lot_selected' ); | |||
// And one has been clicked before… | |||
$( | if ( prevCheckbox !== null && e.shiftKey ) { | ||
method = $thisControl.hasClass( 'cat_a_lot_selected' ) ? 'addClass' : 'removeClass'; | |||
// Check or uncheck this one and all in-between checkboxes | |||
$box.slice( | |||
} | Math.min( $box.index( prevCheckbox ), $box.index( $thisControl ) ), | ||
Math.max( $box.index( prevCheckbox ), $box.index( $thisControl ) ) + 1 | |||
)[ method ]( 'cat_a_lot_selected' ); | |||
} | |||
// Either way, update the prevCheckbox variable to the one clicked now | |||
prevCheckbox = $thisControl; | |||
if ( $.isFunction( cb ) ) { cb(); } | |||
} ); | |||
return $box; | |||
}; | |||
// </ | }( jQuery, mediaWiki ) ); | ||
// </nowiki> |
ویرایش