第1行: |
第1行: |
| /** | | /** |
− | * Cat-a-lot | + | * Cat-a-lot - changes category of multiple files |
− | * Changes category of multiple files
| + | **/ |
− | * | + | if (mw.config.get('wgNamespaceNumber') === 14) { |
− | * @rev 00:13, 10 February 2018 (UTC)
| + | window.catALotPrefs = { editpages: true, subcatcount: 100 }; |
− | * @author Originally by Magnus Manske (2007)
| + | mw.loader.using(['jquery.ui', 'mediawiki.util']).done(function () { |
− | * @author RegExes by Ilmari Karonen (2010)
| + | mw.util.addCSS("#cat_a_lot_settings { display:none !important;}"); // Preferences depend on some Commons gadgets, not available locally |
− | * @author Completely rewritten by DieBuche (2010-2012)
| + | mw.loader.load('//commons.wikimedia.org/w/index.php?title=MediaWiki:Gadget-Cat-a-lot.js&action=raw&ctype=text/javascript'); |
− | * @author Rillke (2012-2014)
| + | mw.loader.load('//commons.wikimedia.org/w/index.php?title=MediaWiki:Gadget-Cat-a-lot.css&action=raw&ctype=text/css', 'text/css'); |
− | * @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:
| |
− | * 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,
| |
− | version: '4.77',
| |
− | 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 () {
| |
− | // 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'] );
| |
− | } );
| |
− |
| |
− | 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; }
| |
− |
| |
− | CAL.autoCompleteIsEnabled = true;
| |
− |
| |
− | 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' )
| |
− | .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', initAutocomplete );
| |
− |
| |
− | if ( !CAL.executed ) {
| |
− | $.when( mw.loader.using( [
| |
− | 'jquery.ui',
| |
− | 'jquery.ui',
| |
− | 'jquery.ui',
| |
− | 'mediawiki.api',
| |
− | '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 ( 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' ); }
| |
− |
| |
− | 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 () {
| |
− | this.selectedLabels = this.labels.filter( '.cat_a_lot_selected:visible' );
| |
− | return this.selectedLabels.map( function () {
| |
− | var label = $( this ), file = label.find( 'a[title][class$="title"]' );
| |
− | 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 () {
| |
− | 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 () {
| |
− | this.labels = $();
| |
− | 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 ) {
| |
− | 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();
| |
− | },
| |
− |
| |
− | getSubCats: function () {
| |
− | var data = {
| |
− | list: 'categorymembers',
| |
− | cmtype: 'subcat',
| |
− | cmlimit: this.settings.subcatcount,
| |
− | cmtitle: 'Category:' + this.currentCategory
| |
− | };
| |
− |
| |
− | 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 () {
| |
− | var data = {
| |
− | prop: 'categories',
| |
− | titles: 'Category:' + this.currentCategory
| |
− | };
| |
− | this.doAPICall( data, function ( result ) {
| |
− | CAL.parentCats = [];
| |
− | var cats,
| |
− | 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';
| |
− | CAL.createCatLinks( '→', [ CAL.currentCategory ], table );
| |
− | $resultList.append( table );
| |
− | return;
| |
− | }
| |
− | // there should be only one, but we don't know its ID
| |
− | for ( var id in pages ) { cats = pages[ id ].categories || []; }
| |
− |
| |
− | for ( var i = 0; i < cats.length; i++ ) { CAL.parentCats.push( cats[ i ].title.replace( /^[^:]+:/, '' ) ); }
| |
− |
| |
− | 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 + ')' );
| |
− | },
| |
− |
| |
− | regexCatBuilder: function ( category ) {
| |
− | var catname = this.localizedRegex( 14, 'Category' );
| |
− |
| |
− | // Build a regexp string for matching the given category:
| |
− | // trim leading/trailing whitespace and underscores
| |
− | category = category.replace( /^[\s_]+|[\s_]+$/g, '' );
| |
− |
| |
− | // escape regexp metacharacters (= any ASCII punctuation except _)
| |
− | category = mw.util.escapeRegExp( category );
| |
− |
| |
− | // any sequence of spaces and underscores should match any other
| |
− | category = category.replace( /[\s_]+/g, '[\\s_]+' );
| |
− |
| |
− | // Make the first character case-insensitive:
| |
− | var first = category.substr( 0, 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):
| |
− | // 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_]*(\\|[^\\]]*(?:\\][^\\]]+)*)?\\]\\]\\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 );
| |
− | },
| |
− |
| |
− | editCategories: function ( result, file, targetcat, mode ) {
| |
− | if ( !result || !result.query ) {
| |
− | // Happens on unstable wifi connections..
| |
− | this.connectionError.push( file[ 0 ] );
| |
− | this.updateCounter();
| |
− | return;
| |
− | }
| |
− | 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
| |
− | if ( mode !== 'remove' && this.regexCatBuilder( targetcat ).test( otext ) ) {
| |
− | // If the new cat is already there, just remove the old one
| |
− | if ( mode === 'move' ) {
| |
− | mode = 'remove';
| |
− | targetcat = sourcecat;
| |
− | } else {
| |
− | this.alreadyThere.push( file[ 0 ] );
| |
− | this.updateCounter();
| |
− | return;
| |
− | }
| |
− | }
| |
− |
| |
− | // Text modification (following 3 functions are partialy taken from HotCat)
| |
− | 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
| |
− | 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 ) {
| |
− | this.notFound.push( file[ 0 ] );
| |
− | this.updateCounter();
| |
− | 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 = {
| |
− | action: 'edit',
| |
− | assert: 'user',
| |
− | summary: sumCmt,
| |
− | title: file[ 0 ],
| |
− | text: text,
| |
− | bot: true,
| |
− | starttimestamp: this.starttimestamp,
| |
− | 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 () {
| |
− | this.counterCurrent++;
| |
− | if ( this.counterCurrent > this.counterNeeded ) { this.displayResult(); } else { this.domCounter.text( this.counterCurrent ); }
| |
− | },
| |
− |
| |
− | displayResult: function () {
| |
− | document.body.style.cursor = 'auto';
| |
− | $.removeSpinner( 'fb-dialog' );
| |
− | this.progressDialog.parent()
| |
− | .addClass( 'cat_a_lot_done' )
| |
− | .find( '.ui-dialog-buttonpane button span' ).eq( 0 )
| |
− | .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.connectionError.length ) {
| |
− | rep.append( '<h5>' + msg( 'skipped-server', this.connectionError.length ) + '</h5>' )
| |
− | .append( this.connectionError.join( '<br>' ) );
| |
− | }
| |
− |
| |
− | },
| |
− |
| |
− | /**
| |
− | * @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.alreadyThere = [];
| |
− | this.connectionError = [];
| |
− | this.counterCurrent = 1;
| |
− | this.counterNeeded = pages.length;
| |
− | 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', 'jquery.spinner', 'mediawiki.util' ], function () {
| |
− | CAL.showProgress();
| |
− | CAL.getTargetCat( pages, targetcat, mode );
| |
− | } );
| |
− | }
| |
− |
| |
− | },
| |
− |
| |
− | 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, table ) {
| |
− | list.sort();
| |
− | var button = ( this.settings.button && mw.loader.getState( 'jquery.ui' ) === 'ready' ) ? 1 : 0;
| |
− | for ( var c = 0; c < list.length; c++ ) {
| |
− | 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 {
| |
− | $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 );
| |
− | }
| |
− | },
| |
− |
| |
− | getCategoryList: function () {
| |
− | this.catCounter = 0;
| |
− | this.getParentCats();
| |
− | 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 () {
| |
− | if ( this.settings.redir_category && this.settings.redir_category === this.parentCats[ 0 ] ) { return this.solveSoftRedirect(); }
| |
− |
| |
− | var table = $( '<table>' );
| |
− |
| |
− | this.createCatLinks( '↑', this.parentCats, table );
| |
− | this.createCatLinks( '→', [ this.currentCategory ], table );
| |
− | // 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';
| |
− |
| |
− | // 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 ) {
| |
− | document.body.style.cursor = 'wait';
| |
− | this.currentCategory = newcat;
| |
− | $resultList.html( '<div class="cat_a_lot_loading">' + mw.msg( 'Wikieditor-loading' ) + '</div>' );
| |
− | 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 () {
| |
− | 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.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 () {
| |
− | if ( $( '.cat_a_lot_enabled' )[ 0 ] ) {
| |
− | 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' );
| |
− |
| |
− | $link.html( $( '<span>' )
| |
− | .text( '×' )
| |
− | .css( { font: 'bold 2em monospace', lineHeight: '.75em' } )
| |
− | );
| |
− | $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' ], 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 );
| |
− |
| |
− | /**
| |
− | * When clicking a cat-a-lot label with Shift pressed, select all labels between the current and last-clicked one.
| |
− | */
| |
− | $.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>
| |