ميدياويكي:Gadget-transclusion-check.js

ملاحظة: بعد النشر، أنت قد تحتاج إلى إفراغ الكاش الخاص بمتصفحك لرؤية التغييرات.

  • فايرفوكس / سافاري: أمسك Shift أثناء ضغط Reload، أو اضغط على إما Ctrl-F5 أو Ctrl-R (⌘-R على ماك)
  • جوجل كروم: اضغط Ctrl-Shift-R (⌘-Shift-R على ماك)
  • إنترنت إكسبلورر/إيدج: أمسك Ctrl أثناء ضغط Refresh، أو اضغط Ctrl-F5
  • أوبرا: اضغط Ctrl-F5.
// ==================================================================
// Scan an Index page's childen for transclusion status
//
// Add a tool to the sidebar on Index pages. When activated, each page in the
// page list is checked for transclusion.
//
// Pages that are transcluded have a CSS class added.
// Pages that are manually tagged as not transcluded ditto.
//
// The accompanying CSS then contains simple logic styling pages based on these
// classes and whether or not they are expected to be transcluded or not.
// ==================================================================

/* eslint-disable one-var, vars-on-top */
// Make sure the necessary modules are loaded
mw.loader.using(['mediawiki.util', 'mediawiki.api'], function () {

	// Only active on Index:-namespace pages.
	if (mw.config.get('wgCanonicalNamespace' ) !== 'Index') {
		return;
	}
    // Only active when in view mode.
    if (mw.config.get('wgAction') !== 'view') {
        return;
    }

	// Wait for the page to be parsed (new-style $(document).ready())
	$(function () { 
		// Add portlet to let the user invoke the check.
    	var checkPortlet = mw.util.addPortletLink(
    		'p-tb', '#', 'Check transclusion', 'ca-checktransclude',
			'Check the transclusion status of each page in this index (shift-click to clear).'
		); 
		$(checkPortlet).on('click', handleClick);
	
		// Also add a check link to the transclusion status
		$('<span id="ws-transclusion-check-inline">فحص</span>')
			.on('click', handleClick)
			.appendTo("#ws-index-transclusion-value");
	
		// And hijack the old link while we migrate to the new tool
		$("#mw-indicator-transclusion_checker, #mw-indicator-transclusion_checker a").on('click', handleClick);
	}); // END: $(document).ready()
}); // END: mw.loader.using()

// Common handling of a user click on any one of the several points of
// invocation, toggle styling, and run the check if relevant.
function handleClick (e) {
	e.preventDefault();
	$('.transcluded').removeClass('transcluded');
	$('.exempt').removeClass('exempt');
	if (e.shiftKey) {
		// Remove transclusion indicator styling
		$('.prp-index-pagelist').removeClass('transclusion-check');
		$('.prp-index-pagelist').removeClass(['transcluded', 'exempt']);
	} else {
		$('.prp-index-pagelist').addClass('transclusion-check');
		checkTransclusion();
	}
}

// Grab all pages listed in the pagelist, divide them into batches matching the
// API limit, and request transclusion status and applied categories from the
// API. Uses a factory function for the API query because the API can return
// partial results that require a nested continue request.
//
// TODO: The API limit may change so we really should have some way to determine
// it dynamically. Or, I suppose, set it as a global(ish) constant up top some
// suitable place if it can't be made dynamic.
function checkTransclusion() {
	var batchSize = 50; // API action=query limit.

	var allPages = $('.prp-index-pagelist-page').map(function() {
		return $(this).attr('title')
			.replace(/ \(page does not exist\)$/, '');
	}).toArray();

	for (var i = 0; i < allPages.length; i += batchSize) {
		var batch = allPages.slice(i, i + batchSize).join('|');
		var query = makeQuery(batch);
		var api = new mw.Api();
		api.get(query).done(createCallback(batch));
	}
}

//
// Create a callback to handle API responses.
//
// The use of a factory function is because API requests that need to be
// continued will have to trigger a new request from the callback; in other
// words we have multiple call sites where this function is needed.
//
// The .bind() is because mw.Api() tramples all over the argument list when it
// calls the callback. To get the necessary parameter to the call site inside
// the callback we have to .bind() a dummy "this" and the "batch" parameter.
function createCallback(batch) {
	return function(batch, data) {
		if (data.hasOwnProperty('continue')) {
			var query = makeQuery(batch, data);
			var api = new mw.Api();
			api.get(query).done(createCallback(batch));
		}
		for (var k in data.query.pages) { // ES6 for…of would be nice…
			var page = data.query.pages[k];
	
			if (page.hasOwnProperty('transcludedin')) {
				$('[title="' + page.title + '"]').addClass('transcluded');
				$('[title="' + page.title + " (page does not exist)" + '"]')
					.addClass('transcluded');
			}
			if (page.hasOwnProperty('categories')) {
				$('[title="' + page.title + '"]').addClass('exempt');
			}
		}
	}.bind(this, batch);
}

//
// Construct a parameter object (associative array) for mw.api().
//
function makeQuery (batch, data) {
	var query = {
		action: 'query',
		titles: batch,
		prop: 'categories|transcludedin',
		tiprop: 'title',
		tinamespace: "0",
		clcategories: "Category:Not transcluded",
		format: 'json',
		formatversion: 2
	};
	if (typeof data !== 'undefined' && data.hasOwnProperty('continue')) {
		$.extend(query, data.continue);
	}
	return query;
}