نقاش ميدياويكي:Gadget-Cat-a-lot.js

التعليق الأخير: قبل 5 سنوات من جار الله
/* Cat-A-Lot - http://commons.wikimedia.org/wiki/MediaWiki_talk:Gadget-Cat-a-lot.js/translating */

var catALot = {
	apiUrl: wgScriptPath + "/api.php",
	searchmode: false,
	version: 2.18,
	setHeight: 300,
	init: function () {
		this.labels = $('#mw-pages').find('li');
		if (this.labels.length < 25){	
		$("body")
		.append('<div id="cat_a_lot">' + '<div id="cat_a_lot_data"><div>' + '<input type="text" id="cat_a_lot_searchcatname" placeholder="' + this.i18n.enterName + '"/>'
		+ '</div><div id="cat_a_lot_category_list"style="white-space:nowrap;"></div>' + '<div id="cat_a_lot_mark_counter"> </div>' + '<div id="cat_a_lot_selections">' + this.i18n.select
		+ ' <a id="cat_a_lot_select_all">' + this.i18n.all + '</a> | ' + '<a id="cat_a_lot_select_none">' + this.i18n.none + '</a>'
		+ '</div></div><div id="cat_a_lot_head">' + '<a id="cat_a_lot_toggle" title="MediaWiki:Gadget-Cat-a-lot">عدل</a></div></div>');

		if (!this.searchmode) $('#cat_a_lot_selections').append('<br><a id="cat_a_lot_remove"><b>' + this.i18n.removeFromCat + '</b></a>');
		$('#cat_a_lot_remove').click(function () {
			catALot.remove();
		});
		$('#cat_a_lot_select_all').click(function () {
			catALot.toggleAll(true);
		});
		$('#cat_a_lot_select_none').click(function () {
			catALot.toggleAll(false);
		});
		$('#cat_a_lot_toggle').click(function () {
			$(this).toggleClass('cat_a_lot_enabled');
			catALot.run();
		});
		$('#cat_a_lot_searchcatname')
			.keypress(function (e) {
				if (e.which == 13)
					catALot.updateCats($(this).val());
			})
			.autocomplete({
				source: function(request, response) {
					catALot.doAPICall({action:'opensearch', search: request.term, namespace: 14},
						function(data){
							if(data[1])
								response($(data[1]).map(function(index,item){return item.replace(/.*:/, '');}));
						}
					);
				},
				open:function(){
					$(".ui-autocomplete")
						.position({
							my: "left bottom",
							at: "left top",
							of: $('#cat_a_lot_searchcatname')
						});
				}
		});
		importStylesheet('MediaWiki:Gadget-Cat-a-lot.css');
		this.localCatName = mw.config.get('wgFormattedNamespaces')[14];
	}},
	findAllLabels: function () {
		this.labels = $('#mw-pages').find('li');
		var subCats =  $('#mw-subcategories').find('.CategoryTreeItem');
		for (var sub = 0; sub < subCats.length; sub++) {
			var a = $(subCats[sub]).find('a');
			if (a.length) {
				a[0]['title'] = catALot.localCatName + ":" + a[0].innerHTML;
				this.labels.push(subCats[sub]);
			}
		}
		$('li a, .CategoryTreeLabel', mw.util.$content).attr({href: '#noSuchAnchor'});
	},

	getMarkedLabels: function () {
		var marked = [];
		this.selectedLabels = this.labels.filter('.cat_a_lot_selected');
		this.selectedLabels.each(function () {
			var file = $(this).find('a[title]');
			marked.push([file.attr('title'), $(this)]);
		});
		return marked;
	},

	updateSelectionCounter: function () {
		this.selectedLabels = this.labels.filter('.cat_a_lot_selected');
		$('#cat_a_lot_mark_counter').show().html(this.selectedLabels.length + this.i18n.filesSelected );
	},

	makeClickable: function () {
		this.findAllLabels();
		this.labels.click(function () {
			$(this).toggleClass('cat_a_lot_selected');
			catALot.updateSelectionCounter();
		});
	},

	toggleAll: function (select) {
		this.labels.toggleClass('cat_a_lot_selected', select);
		this.updateSelectionCounter();
	},

	getSubCats: function (cmcontinue) {
		var data = {
			action: 'query',
			list: 'categorymembers',
			cmnamespace: 14,
			cmlimit: 50,
			cmtitle: this.localCatName + ':' + this.currentCategory
		};
		if (cmcontinue)
			data.cmcontinue = cmcontinue;
		else
			this.subCats = [];

		this.doAPICall(data, function (result) {

			var cats = result.query.categorymembers;

			for (var i = 0; i < cats.length; i++) {
				catALot.subCats.push(cats[i].title.replace(/^[^:]+:/, ""));
			}
			if (result['query-continue'])
				catALot.getSubCats(result['query-continue'].categorymembers.cmcontinue);
			else {
				catALot.catCounter++;
				if (catALot.catCounter == 2) catALot.showCategoryList();
			}
		});
	},


	getParentCats: function () {
		var data = {
			action: 'query',
			prop: 'categories',
			cllimit: 500,
			titles: this.localCatName + ':' + this.currentCategory
		};
		this.doAPICall(data, function (result) {
			catALot.parentCats = new Array();
			var pages = result.query.pages;
			if (pages[-1] && pages[-1].missing == '') {
				catALot.catlist.html('<span id="cat_a_lot_no_found">' + catALot.i18n.catNotFound + '</span>');
				document.body.style.cursor = 'auto';

				catALot.catlist.append('<ul></ul>');
				catALot.createCatLinks("→", [catALot.currentCategory]);
				return;
			}
			// there should be only one, but we don't know its ID
			for (var id in pages) {
				var cats = pages[id].categories;
			}
			for (var i = 0; i < cats.length; i++) {
				catALot.parentCats.push(cats[i].title.replace(/^[^:]+:/, ""));
			}

			catALot.catCounter++;
			if (catALot.catCounter == 2) catALot.showCategoryList();
		});
	},
	regexBuilder: function (category) {
		var catname = ( this.localCatName == 'Category' ) ? this.localCatName: this.localCatName + '|Category';
		catname = '(' + catname + ')';

		// Build a regexp string for matching the given category:
		// trim leading/trailing whitespace and underscores
		category = category.replace(/^[\s_]+/, "").replace(/[\s_]+$/, "");

		// escape regexp metacharacters (= any ASCII punctuation except _)
		category = category.replace(/([!-\/:-@\[-^`{-~])/g, '\\$1');

		// 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_]*(\\|[^\\]]*(?:\\][^\\]]+)*)?\\]\\]', 'ig');
	},

	getContent: function (file, targetcat, mode) {

		var data = {
			action: 'query',
			prop: 'info|revisions',
			rvprop: 'content|timestamp',
			intoken: 'edit',
			titles: file[0]
		};

		this.doAPICall(data, function (result) {
			catALot.editCategories(result, file, targetcat, mode);
		});
	},

	editCategories: function (result, file, targetcat, mode) {

		if (result == null) {
			//Happens on unstable wifi connections..
			this.connectionError.push(file[0]);
			this.updateCounter();
			return;
		}
		var pages = result.query.pages;

		// there should be only one, but we don't know its ID
		for (var id in pages) {
			// The edittoken only changes between logins
			this.edittoken = pages[id].edittoken;
			var otext = pages[id].revisions[0]['*'];
			var starttimestamp = pages[id].starttimestamp;
			var timestamp = pages[id].revisions[0]['timestamp'];
		}


		var sourcecat = wgTitle;

		// Check if that file is already in that category
		if (mode != "remove" && this.regexBuilder(targetcat).test(otext)) {
			//If the new cat is already there, just remove the old one.
			if (mode == 'move') {
				mode='remove';
			} else {
				this.alreadyThere.push(file[0]);
				this.updateCounter();
				return;
			}
		}

		var text = otext;
		var comment;

		// Fix text
		switch (mode) {
		case 'add':
			text += "\n[[" + this.localCatName + ":" + targetcat.replace("تصنيف:","") + "]]\n";
                        text=text.replace("تصنيف:تصنيف:","تصنيف:");
			comment = this.i18n.summaryAdd + targetcat.replace("تصنيف:","") + "]]";
			break;
		case 'copy':
			text = text.replace(this.regexBuilder(sourcecat), "[[" + this.localCatName + ":" + sourcecat + "$2]]\n[[" + this.localCatName + ":" + targetcat.replace("تصنيف:","") + "$2]]");
                        text=text.replace("تصنيف:تصنيف:","تصنيف:");
			comment = this.i18n.summaryCopy + sourcecat + "]] " + this.i18n.to + targetcat.replace("تصنيف:","") + "]]";
			break;
		case 'move':
			text = text.replace(this.regexBuilder(sourcecat), "[[" + this.localCatName + ":" + targetcat.replace("تصنيف:","") + "$2]]");
                        text=text.replace("تصنيف:تصنيف:","تصنيف:");
			comment = this.i18n.summaryMove + sourcecat + "]] " + this.i18n.to + targetcat.replace("تصنيف:","") + "]]";
			break;
		case 'remove':
			text = text.replace(this.regexBuilder(sourcecat), "");
                        text=text.replace("تصنيف:تصنيف:","تصنيف:");
			comment = this.i18n.summaryRemove + sourcecat + "]]";
			break;
		}

		if (text == otext) {
			this.notFound.push(file[0]);
			this.updateCounter();
			return;
		}

		var data = {
			action: 'edit',
			summary: comment,
			title: file[0],
			token: this.edittoken,
			starttimestamp: starttimestamp,
			basetimestamp: timestamp,
			text: text
		};

		var isBot=$.inArray('bot', wgUserGroups)>-1;
		if(isBot)
			data.bot = '1';

		this.doAPICall(data, function (ret) {
			catALot.updateCounter();
		});
		this.markAsDone(file[1], mode, targetcat);
	},
	markAsDone: function (label, mode, targetcat) {

		label.addClass('cat_a_lot_markAsDone');
		switch (mode) {
		case 'add':
			label.append('<br>' + this.i18n.addedCat + ' ' + targetcat);
			break;
		case 'copy':
			label.append('<br>' + this.i18n.copiedCat + ' ' + targetcat);
			break;
		case 'move':
			label.append('<br>' + this.i18n.movedCat + ' ' + targetcat);
			break;
		case 'remove':
			label.append('<br>' + this.i18n.removedCat );
			break;
		}
	},
	updateCounter: function () {

		this.counterCurrent++;
		if (this.counterCurrent > this.counterNeeded) this.displayResult();
		else this.domCounter.text(this.counterCurrent);
	},

	displayResult: function () {

		document.body.style.cursor = 'auto';
		$('.cat_a_lot_feedback').addClass('cat_a_lot_done');
		$('.ui-dialog-content').height('auto');
		var rep = this.domCounter.parent();
		rep.html('<h3>' + this.i18n.done + '</h3>');
		rep.append( this.i18n.allDone );

		var close = $('<a style="color: #0645ad;">').append( this.i18n.returnToPage );
		close.click(function () {
			catALot.progressDialog.remove();
			catALot.toggleAll(false);
		});
		rep.append(close);
		if (this.alreadyThere.length) {
			rep.append( this.i18n.skippedAlready );
			rep.append(this.alreadyThere.join('<br>'));
		}
		if (this.notFound.length) {
			rep.append( this.i18n.skippedNotFound );
			rep.append(this.notFound.join('<br>'));
		}
		if (this.connectionError.length) {
			rep.append( this.i18n.skippedServer );
			rep.append(this.connectionError.join('<br>'));
		}

	},

	moveHere: function (targetcat) {
		this.doSomething(targetcat, 'move');
	},

	copyHere: function (targetcat) {
		this.doSomething(targetcat, 'copy');
	},

	addHere: function (targetcat) {
		this.doSomething(targetcat, 'add');
	},

	remove: function () {
		this.doSomething('', 'remove');
	},

	doSomething: function (targetcat, mode) {
		var files = this.getMarkedLabels();
		if (files.length == 0) {
			alert( this.i18n.noneSelected );
			return;
		}
		this.notFound = [];
		this.alreadyThere = [];
		this.connectionError = [];
		this.counterCurrent = 1;
		this.counterNeeded = files.length;
		this.showProgress();
		for (var i = 0; i < files.length; i++) {
			this.getContent(files[i], targetcat, mode);
		}
	},

	doAPICall: function (params, callback) {

		params.format = 'json';
		$.ajax({
			url: this.apiUrl,
			cache: false,
			dataType: 'json',
			data: params,
			type: 'POST',
			success: callback
		});
	},

	createCatLinks: function (symbol, list) {
		list.sort();
		var domlist = this.catlist.find('ul');
		for (var i = 0; i < list.length; i++) {
			var li = $('<li></li>');

			var link = $('<a></a>');
			link.text(list[i]);
			li.data('cat', list[i]);
			link.click(function () {
				catALot.updateCats($(this).parent().data('cat'));
			});

			if (this.searchmode) {
				var add = $('<a class="cat_a_lot_action"><b>' + this.i18n.add + '</b></a>');
				add.click(function () {
					catALot.addHere($(this).parent().data('cat'));
				});
			} else {
				var move = $('<a class="cat_a_lot_move"><b>' + this.i18n.move + '</b></a>');
				move.click(function () {
					catALot.moveHere($(this).parent().data('cat'));
				});

				var copy = $('<a class="cat_a_lot_action"><b>' + this.i18n.copy + '</b></a>');
				copy.click(function () {
					catALot.copyHere($(this).parent().data('cat'));
				});
			}

			// Can't move to source category
			if (list[i] != wgTitle && this.searchmode) li.append(' ').append(add);
			else if (list[i] != wgTitle && !this.searchmode) li.append(' ').append(move).append(' ').append(copy);
			li.append(symbol).append(' ').append(link);

			domlist.append(li);
		}
	},

	getCategoryList: function () {
		this.catCounter = 0;
		this.getParentCats();
		this.getSubCats();
	},

	showCategoryList: function () {
		var thiscat = [this.currentCategory];

		this.catlist.empty();
		this.catlist.append('<ul></ul>');

		this.createCatLinks("↑", this.parentCats);
		this.createCatLinks("→", thiscat);
		this.createCatLinks("↓", this.subCats);

		document.body.style.cursor = 'auto';
		//Reset width
		var cat = $('#cat_a_lot');
		cat.width('');
		cat.height('');
		cat.width(cat.width() * 1.3);
		var list = $('#cat_a_lot_category_list');
		list.css({maxHeight: this.setHeight+'px', height: ''});
	},

	updateCats: function (newcat) {
		document.body.style.cursor = 'wait';

		this.currentCategory = newcat;
		this.catlist = $('#cat_a_lot_category_list');
		this.catlist.html('<div class="cat_a_lot_loading">' + this.i18n.loading + '</div>');
		this.getCategoryList();
	},
	showProgress: function () {
		document.body.style.cursor = 'wait';

		this.progressDialog = $('<div></div>')
		.html( this.i18n.editing + ' <span id="cat_a_lot_current">' + this.counterCurrent + '</span> ' + this.i18n.of + this.counterNeeded)
		.dialog({
			width: 450,
			height: 90,
			minHeight: 90,
			modal: true,
			resizable: false,
			draggable: false,
			closeOnEscape: false,
			dialogClass: "cat_a_lot_feedback"
		});
		$('.ui-dialog-titlebar').hide();
		this.domCounter = $('#cat_a_lot_current');

	},

	run: function () {
		if ($('.cat_a_lot_enabled').length) {
			this.makeClickable();
			$("#cat_a_lot_data").show();
			$('#cat_a_lot').resizable({
				handles: 'n', 
				alsoResize: '#cat_a_lot_category_list',
				resize: function(event, ui) {
					$(this).css({left:"", top:""});
					catALot.setHeight = $(this).height();
					$('#cat_a_lot_category_list').css({maxHeight: '', width: ''});
				} 
			});
			$('#cat_a_lot_category_list').css({maxHeight: '300px'});
			if (this.searchmode) this.updateCats("Pictures and images");
			else this.updateCats(wgTitle);

		} else {
			$("#cat_a_lot_data").hide();
			$("#cat_a_lot").resizable( "destroy" );
			//Unbind click handlers
			this.labels.unbind('click');
		}
	},
	i18n: (wgUserLanguage == "ar") ? {
                loading: 'تحميل...',
		editing: 'تعديل صفحات: ',
		of: 'من ',
		skippedAlready: '<h5>لم تعدل الصفحات التالية لوجودها في التصنيف مسبقا:</h5>',
		skippedNotFound: '<h5>لم تعدل الصفحات التالية لعدم إيجاد التصنيف القديم فيها:</h5>',
		skippedServer: '<h5>لم تعدل الصفحات التالية لوجود مشكل في الاتصال بالخادم:</h5>',
		allDone: 'عدلت الصفحات. ',
		done: 'نفذ',
		addedCat: 'أضيف تصنيف',
		copiedCat: 'نسخ إلى تصنيف',
		movedCat: 'نقل إلى تصنيف',
		removedCat: 'أزيل من تصنيف',
		returnToPage: 'ارجع لصفحة التصنيف.',
		catNotFound: 'تصنيف غير موجود',


		//as in 17 files selected
		filesSelected: ' عدد تصنيفات التي اخترتها.',

		//Actions
		copy: 'انسخ',
		move: 'انقل',
		add: 'أضف',
		removeFromCat: 'أزل من التصنيف',
		enterName: 'أدخل اسم التصنيف',
		select: 'اختر',
		all: 'الكل',
		none: 'لا شيء',

		noneSelected: 'لم تختر أي تصنيف',

		//Summaries:
		summaryAdd: '[[ميدياويكي:Gadget-Cat-a-lot|تعديل تصنيفات]]: إضافة [[تصنيف:',
		summaryCopy: '[[ميدياويكي:Gadget-Cat-a-lot|تعديل تصنيفات]]: نسخ من [[تصنيف:',
		to: 'إلى [[تصنيف:',
		summaryMove: '[[ميدياويكي:Gadget-Cat-a-lot|تعديل تصنيفات]]: نقل من [[تصنيف:',
		summaryRemove: '[[ميدياويكي:Gadget-Cat-a-lot|تعديل تصنيفات]]: إزالة من [[تصنيف:'
	}:	{
		//Progress
		loading: 'Loading...',
		editing: 'Editing page',
		of: 'of ',
		skippedAlready: '<h5>The following pages were skipped, because the page was already in the category:</h5>',
		skippedNotFound: '<h5>The following pages were skipped, because the old category could not be found:</h5>',
		skippedServer: '<h5>The following pages couldn\'t be changed, since there were problems connecting to the server:</h5>',
		allDone: 'All pages are processed.',
		done: 'Done!',
		addedCat: 'Added category',
		copiedCat: 'Copied to category',
		movedCat: 'Moved to category',
		removedCat: 'Removed from category',
		returnToPage: 'Return to page',
		catNotFound: 'Category not found.',


		//as in 17 files selected
		filesSelected: ' files selected.',

		//Actions
		copy: 'Copy',
		move: 'Move',
		add: 'Add',
		removeFromCat: 'Remove from this category',
		enterName: 'Enter category name',
		select: 'Select',
		all: 'all',
		none: 'none',

		noneSelected: 'No files selected!',

		//Summaries:
		summaryAdd: 'Cat-a-lot: Adding [[Category:',
		summaryCopy: 'Cat-a-lot: Copying from [[Category:',
		to: 'to [[Category:',
		summaryMove: 'Cat-a-lot: Moving from [[Category:',
		summaryRemove: 'Cat-a-lot: Removing from [[Category:'
	}
};


if ((wgNamespaceNumber == -1 && wgCanonicalSpecialPageName == "Search") || wgNamespaceNumber == 14) {
	if ( wgNamespaceNumber == -1 ) catALot.searchmode = true;
	mediaWiki.loader.using(['jquery.ui.dialog', 'jquery.ui.autocomplete'], function () {
		$(function () {
			catALot.init();
		});
	});
}
مرحباً أخي @ASammour: يبدو التحديد لا يشمل إزالة التصانيف، يرجى مراجعة الكود لو سمحت، تحياتي لك.--جار الله (نقاش) 23:11، 28 أكتوبر 2018 (ت ع م)ردّ
أهلًا أخي @جار الله: الكود يقوم بإخفاء المربع الأصفر الذي يظهر في أسفل يسار الصفحة ("عدل")؛ ويعني ذلك عدم النقل، أو الإزالة في حال كون عدد صفحات التصنيف أكثر من 30. هل تريد أن تستثني الإزالة من هذا الشرط؟. أرجو التوضيح بعض الشيء. تحياتي.--ASammour (نقاش) 23:35، 28 أكتوبر 2018 (ت ع م)ردّ
@ASammour: عذراً أخي المشكلة لم تكن في الكود، المشكلة كانت بقيام الزميل @علاء: بإضافة الكود القديم بصفحته مما سمح له التعديل أكثر من العدد المحدد، تحياتي لك.--جار الله (نقاش) 23:41، 28 أكتوبر 2018 (ت ع م)ردّ
عُد إلى صفحة "Gadget-Cat-a-lot.js".