Użytkownik:Expert3222/quickPatrol.js

Z Nondanych
Wersja z dnia 15:29, 15 maj 2020 autorstwa Eksekk (dyskusja • edycje) (na razie kopia)
(różn.) ← poprzednia wersja • przejdź do aktualnej wersji (różn.) • następna wersja → (różn.)

Uwaga: aby zobaczyć zmiany po opublikowaniu, może zajść potrzeba wyczyszczenia pamięci podręcznej przeglądarki.

  • Firefox / Safari: Przytrzymaj Shift podczas klikania Odśwież bieżącą stronę, lub naciśnij klawisze Ctrl+F5, lub Ctrl+R (⌘-R na komputerze Mac)
  • Google Chrome: Naciśnij Ctrl-Shift-R (⌘-Shift-R na komputerze Mac)
  • Internet Explorer / Edge: Przytrzymaj Ctrl, jednocześnie klikając Odśwież, lub naciśnij klawisze Ctrl+F5
  • Opera: Naciśnij klawisze Ctrl+F5.
/*
 * gadżet do szybkiego oznaczania wersji jako sprawdzonych na OZ, nowych stronach i widoku diffów
 * autor: Expert3222
 * drobne modyfikacje: Ostrzyciel
 */

// TODO: $(this) na $this (do zmiennej), lepsza wydajność będzie

var QuickPatrolLastClick = 0; //https://stackoverflow.com/questions/28609994/javascript-prevent-function-from-executing-multiple-times
var QuickPatrolDelay = 20; //syfiło w logach, rejestrując patrolowanie po kilka razy
var QuickPatrolToken = null;
var QuickPatrolDiffPageRevids = [];
var spinnerCode = "<div class='mw-spinner mw-spinner-small mw-spinner-inline' title='...'></div>";

//fix dla
//https://nonsa.pl/wiki/Plik:Patrolowe_czworaczki.jpg
var makePatrolLinksIsExecuting = false;
var makeDiffViewRevidsListIsExecuting = false;
var makeFilePatrolLinkCallCount = 0;

$(document).ready(function()
{
	if (mw.config.get("wgCanonicalSpecialPageName") === "Recentchanges")
	{
		makePatrolLinks();
		if (!!$('.mw-rcfilters-enabled').length) //wzięte od Polskacafe z gadżetu RollWithReason
		{
			setInterval(makePatrolLinks, 2000);
		}
	}
	else if (mw.config.get("wgCanonicalSpecialPageName") == "Newpages")
	{
		makeNewpagesPatrolLinks();
	}
	else if (mw.util.getParamValue("diff") !== null)
	{
		var parse = function()
		{
			var oldid = mw.util.getParamValue("oldid", $("#mw-diff-otitle1 > strong > a").attr("href"));
			//alert("oldid = " + oldid);
			var curid = mw.util.getParamValue("oldid", $("#mw-diff-ntitle1 > strong > a").attr("href"));
			//alert("curid = " + curid);
			
			var title = mw.config.get("wgPageName").replace("_", " ");
			
			//console.log(oldid, curid, title);
			makeDiffViewRevidsList(oldid, curid, title);
		};
		parse();
		
		setInterval(parse, 2000); //"przeglądaj historię interaktywnie" na górze
	}
	else if (mw.config.get("wgCanonicalSpecialPageName") == "MobileDiff")
	{
		//TODO
		
		/*var oldid = mw.util.getParamValue("oldid", $("#mw-mf-diff-info a").attr("href"));
		alert("oldid = " + oldid);
		var urlArray = window.location.href.split("/");
		var curid = urlArray[urlArray.length - 1];
		alert("curid = " + curid);
		
		var title = $("#mw-mf-diff-info a").text();
		makeDiffViewRevidsList(oldid, curid, title);*/
	}
});

function makePatrolLinks()
{
	if (makePatrolLinksIsExecuting)
	{
		return;
	}
	makePatrolLinksIsExecuting = true;
	
	//podstawowa logika: mamy liczbę zaplanowanych wywołań AJAX i liczbę zakończonych. Chcemy zaznaczyć, że funkcja skończyła się wykonywać, gdy obie wartości będą równe.
	//ale czysto teoretycznie może dojść do sytuacji, w której liczba zaplanowanych zapytań nie będzie tą finalną, a obie będą równe.
	//dlatego sprawdzamy tylko wtedy, gdy kontrola doszła do końca funkcji (czyli wiemy finalnie, ile AJAX-ów będzie)
	var AJAXCallsCompleted = 0;
	var reachedEndOfFunction = false;
	
	function handleEndOfAJAXCall()
	{
		++AJAXCallsCompleted;
		if (AJAXCallsCompleted == makeFilePatrolLinkCallCount && reachedEndOfFunction)
		{
			makePatrolLinksIsExecuting = false;
			makeFilePatrolLinkCallCount = 0;
		}
	}
	//pojedyncze zmiany i pojedyncze przesłania pliku
	$("table.mw-changeslist-reviewstatus-unpatrolled").each(function()
	{
		if ($(this).find(".patrolButton").length === 0 && $(this).find(".patrolButtonClicked").length === 0) //jeśli nie ma buttona i nie został wcześniej użyty
		{
			if (typeof $(this).attr("data-mw-logaction") == "undefined") //zmiana
			{
				$(this).find(".mw-changeslist-links:not(.mw-usertoollinks)").append("<span class='patrolButton' title='Oznacz tę zmianę jako sprawdzoną' data-diff='" + $(this).attr("data-mw-revid") + "'><a>patrol</a></span>");
			}
			else //przesłanie pliku
			{
				var extraAttributes = 
				{
					"data-mw-logid": $(this).attr("data-mw-logid"),
					"data-mw-ts": $(this).attr("data-mw-ts")
				};
				var overwritten = ($(this).attr("data-mw-logaction") == "upload/upload") ? false : true;
				makeFilePatrolLink($(this), overwritten, extraAttributes).always(handleEndOfAJAXCall);
			}
		}
	});

	//wielokrotne zmiany, które można rozwijać i zwijać + przesłane pliki, też wielokrotne
	$("tr.mw-enhanced-rc.mw-changeslist-reviewstatus-unpatrolled").each(function()
	{
		if ($(this).find(".patrolButton").length === 0 && $(this).find(".patrolButtonClicked").length === 0)
		{
			var found = $(this).find(".mw-changeslist-diff");
			if (found.length > 0) //jest link w "poprz", czyli strona nie została utworzona w tej edycji
			{	
				$(found).after(" • <span class='patrolButton' title='Oznacz tę zmianę jako sprawdzoną' data-diff='" + $(this).attr("data-mw-revid") + "'><a>patrol</a></span>");
			}
			else if (typeof $(this).attr("data-mw-logaction") == "undefined") //strony stworzone w tej edycji
			{
				$(this).html($(this).html().replace("•&nbsp;poprz.", "•&nbsp;poprz. • <span class='patrolButton' title='Oznacz tę zmianę jako sprawdzoną' data-diff='" + $(this).attr("data-mw-revid") + "'><a>patrol</a></span>"));
			}
			else if ($(this).attr("data-mw-logaction") == "upload/upload") //przesłane pliki
			{
				var extraAttributes = 
				{
					"data-mw-logid": $(this).attr("data-mw-logid"),
					"data-mw-ts": $(this).attr("data-mw-ts")
				};
				makeFilePatrolLink($(this), false, extraAttributes).always(handleEndOfAJAXCall);
			}
			else if ($(this).attr("data-mw-logaction") == "upload/overwrite") //nadpisane pliki
			{
				var extraAttributes = 
				{
					"data-mw-logid": $(this).attr("data-mw-logid"),
					"data-mw-ts": $(this).attr("data-mw-ts")
				};
				makeFilePatrolLink($(this), true, extraAttributes).always(handleEndOfAJAXCall);
			}
		}
	});
	
	//link do patrolowania wszystkich zmian danej strony
	$("table.mw-collapsible > tbody").each(function()
	{
		var lines = $(this).find("tr");
		var firstLine = $(lines).first();
		if (lines.length >= 2 && $(firstLine).hasClass("mw-changeslist-reviewstatus-unpatrolled") &&
		    $(firstLine).find(".multiplePatrolButton").length === 0 && $(firstLine).find(".multiplePatrolButtonClicked").length === 0)
		{
			$(firstLine).find("span.mw-changeslist-links > span:nth-child(2)").after("<span class='multiplePatrolButton' title='Oznacz zmiany tej strony jako sprawdzone'><a>patroluj tę stronę</a></span>");
			$(firstLine).find(".multiplePatrolButton").on("click", function()
			{
				patrolMultipleChanges($(lines).parent());
			});
		}
	});

	$(".patrolButton").on("click", function()
	{
		patrolChange($(this).attr("data-diff"), false, false);
	});
	if (makeFilePatrolLinkCallCount === 0 || AJAXCallsCompleted == makeFilePatrolLinkCallCount)
	{
		makePatrolLinksIsExecuting = false;
		makeFilePatrolLinkCallCount = 0;
	}
	reachedEndOfFunction = true;
}

function makeFilePatrolLink(elem, overwritten, extraAttributes)
{
	//console.log(elem);
	++makeFilePatrolLinkCallCount;
	/*console.log(makeFilePatrolLinkCallCount);
	console.dir(elem[0]);*/
	
	var api = new mw.Api(),
		fileName = $(elem).find(".mw-changeslist-log-entry > a:not(.mw-userlink)").attr("title"),
		deferred = $.Deferred();
	
	if (fileName.indexOf(" (strona nie istnieje)") == -1) //jeśli plik istnieje (w przeciwnym przypadku przycisk patrolowania jest zbędny)
	{
		if (!overwritten)
		{
			//bierzemy sobie numer pierwszej wersji pliku, który jest potrzebny do patrolowania
			api.post(
			{
				action: 'query',
				format: 'json',
				formatversion: '2',
				prop: 'revisions',
				titles: fileName,
				rvprop: 'ids',
				rvlimit: '1',
				rvdir: 'newer'
			}).done(function(data)
			{
				var oldid = data.query.pages[0].revisions[0].revid;
				$(elem).find(".mw-changeslist-log-entry > a:not(.mw-userlink)").after("&nbsp;(<span class='patrolButton' title='Oznacz tę zmianę jako sprawdzoną' data-diff='" + oldid + "'><a>patrol</a></span>)");
				
				var $button = $(elem).find(".patrolButton");
				$button.on("click", function()
				{
					patrolChange(oldid, false, false);
				});
				$.each(extraAttributes, function(name, value)
				{
					$button.attr(name, value);
				});
				deferred.resolve();
			}).fail(function(error)
			{
				console.log(error);
				deferred.reject();
			});
		}
		else
		{
			//bierzemy numer konkretnej wersji pliku (takie obejście potrzebne, bo tylko w tym wypadku nie ma atrybutu data-mw-revid)
			var timestamp = $(elem).attr("data-mw-ts");
			api.post(
			{
				action: 'query',
				format: 'json',
				formatversion: '2',
				prop: 'revisions',
				titles: fileName,
				rvprop: 'ids',
				rvlimit: '1',
				rvstart: timestamp,
				rvend: timestamp
			}).done(function(data)
			{
				var oldid = data.query.pages[0].revisions[0].revid;
				$(elem).find("td>a:nth-child(5)").after("&nbsp;(<span class='patrolButton' data-diff='" + oldid + "'><a>patrol</a></span>)");
				$(elem).find(".patrolButton").on("click", function()
				{
					patrolChange(oldid, false, false);
				});
				deferred.resolve();
			}).fail(function(error)
			{
				console.log(error);
				deferred.reject();
			});
		}
	}
	else
	{
		deferred.resolve();
	}
	return deferred.promise();
}

function makeNewpagesPatrolLinks()
{
	$("li.not-patrolled").each(function()
	{
		if (typeof $(this).find(".mw-newpages-oldtitle") == "undefined")
		{
			var oldid = $(this).attr("data-mw-revid");
			$(this).find("span.mw-newpages-history a").after(" • <span class='patrolButton' title='Oznacz tę stronę jako sprawdzoną' data-diff='" + oldid + "'><a>patrol</a></span>");
		}
		else
		{
			var api = new mw.Api(),
				elem = $(this),
				pageName = $(this).find(".mw-newpages-pagename").attr("title");
			
			api.post(
			{
				action: 'query',
				format: 'json',
				formatversion: '2',
				prop: 'revisions',
				titles: pageName,
				rvprop: 'ids',
				rvlimit: '1',
				rvdir: 'newer'
			}).done(function(data)
			{
				var oldid = data.query.pages[0].revisions[0].revid;
				$(elem).find("span.mw-newpages-history a").after(" • <span class='patrolButton' title='Oznacz tę stronę jako sprawdzoną' data-diff='" + oldid + "'><a>patrol</a></span>");
				$(elem).find(".patrolButton").on("click", function()
				{
					patrolChange(oldid, false, false);
				});
			}).fail(function(error)
			{
				console.log(error);
			});
		}
	});
	
	$(".patrolButton").on("click", function()
	{
		patrolChange($(this).attr("data-diff"), false, false);
	});
}

function patrolChange(diff, isDiffPagePatrol, tokenAlreadyReset)
{
	if (QuickPatrolLastClick >= (Date.now() - QuickPatrolDelay))
    	return;
	QuickPatrolLastClick = Date.now();
	
	if (diff === null || diff === "" || typeof diff == "undefined")
	{
		return $.when(); //resolved promise
	}
	
	var api = new mw.Api(),
		$button = undefined, //button to nasz przycisk do patrolowania
		deferred = $.Deferred();
		
	if (!isDiffPagePatrol)
	{
		$button = $(".patrolButton[data-diff='" + diff + "']");
		$button.off("click").find("a").replaceWith(spinnerCode);
	}
	
	api.post(
	{
		action: 'patrol',
		format: 'json',
		revid: diff,
		token: mw.user.tokens.get("patrolToken")
	}).done(function(data)
	{
		if (!isDiffPagePatrol)
		{
			if (typeof $button.attr("data-mw-ts") == "undefined") //patrol zwykłej strony
			{
				$("table[data-mw-revid='" + diff + "'] abbr.unpatrolled").replaceWith('&nbsp;');
				$("tr[data-mw-revid='" + diff + "'] abbr.unpatrolled").replaceWith('&nbsp;');
			}
			else //patrol pliku
			{
				var timestamp = $button.attr("data-mw-ts"),
					logid	  = $button.attr("data-mw-logid");
				
				$("table[data-mw-ts=" + timestamp + "] tr[data-mw-logid=" + logid + "] abbr.unpatrolled").replaceWith('&nbsp;');
			}
			
			var $anchor = $button.parent(); //koniecznie, bo zaraz $button nie bedzie mialo wartosci
			$button.replaceWith("<span><img src='https://nonsa.pl/images/2/26/Za.svg.png' width='15px' height='15px' />&nbsp;<span style='color: green;' class='patrolButtonClicked'>zrobione</span><span>");
			
			processGroupMarksAndButtons($anchor);
		}
		
		deferred.resolve();
	}).fail(function(error)
	{
		if (!tokenAlreadyReset)
		{
			console.log("TOKEN PATROLOWANIA NIEPRAWIDŁOWY - DRUGA PRÓBA");
			api.post
			({
				format: "json",
				formatversion: "2",
				action: "query",
				meta: "tokens",
				type: "patrol"
			}).done(function(data)
			{
				mw.user.tokens.set("patrolToken", data.query.tokens.patroltoken);
				patrolChange(diff, isDiffPagePatrol, true)
				.done(function()
				{
					deferred.resolve();
					console.log("NOWY TOKEN PRAWIDŁOWY");
				}).fail(function()
				{
					deferred.reject();
					console.log("NOWY TOKEN NIEPRAWIDŁOWY");
				});
			});
		}
		else // w przypadku pierwszego faila to zostanie wykonane przez funkcję wywołaną rekursywnie
		{
			if (!isDiffPagePatrol)
			{
				$button.replaceWith("<img src='https://nonsa.pl/images/a/a8/Przeciw.svg.png' width='15px' height='15px' />&nbsp;<span style='color: red;' class='patrolButtonClicked'>błąd</span>");
			}
			console.log(error);
			
			deferred.reject();
		}
	});
	
	return deferred.promise();
}

function patrolMultipleChanges(tbody)
{
	var deferred = $.Deferred(),
		failureCount = 0,
		successCount = 0,
		multiplePatrolButton = tbody.find(".multiplePatrolButton"),
		buttons = $(tbody).find(".patrolButton"),
		buttonsCount = buttons.length;
		
	multiplePatrolButton.off("click").find("a").replaceWith(spinnerCode);
	buttons.each(function()
	{
		QuickPatrolLastClick = 0;
		patrolChange($(this).attr("data-diff"), false, false)
		.done(function()
		{
			++successCount;
		}).fail(function()
		{
			++failureCount;
		}).always(function()
		{
			if (successCount == buttonsCount) //wszystkie wywołania AJAX do patrolowania zakończyły się pomyślnie
			{
				deferred.resolve(successCount, failureCount);
					
				multiplePatrolButton.replaceWith("<span><img src='https://nonsa.pl/images/2/26/Za.svg.png' width='15px' height='15px' />&nbsp;<span style='color: green;' class='multiplePatrolButtonClicked'>zrobione</span></span>");
				tbody.find("tr:first-child abbr.unpatrolled").replaceWith("&nbsp;");
			}
			else if (successCount + failureCount == buttonsCount) //wszystkie zakończyły się, niekoniecznie pomyślnie
			{
				deferred.reject(successCount, failureCount);
				
				multiplePatrolButton.replaceWith("<img src='https://nonsa.pl/images/a/a8/Przeciw.svg.png' width='15px' height='15px' />&nbsp;<span style='color: red;' class='multiplePatrolButtonClicked'>błąd</span>");
			}
		});
	});
	
	if (buttonsCount == 0)
	{
		multiplePatrolButton.replaceWith("<span><img src='https://nonsa.pl/images/2/26/Za.svg.png' width='15px' height='15px' />&nbsp;<span style='color: green;' class='multiplePatrolButtonClicked'>zrobione</span></span>");
		tbody.find("tr:first-child abbr.unpatrolled").replaceWith("&nbsp;");
	}
	
	return deferred.promise();
}

function makeDiffViewRevidsList(oldid, curid, pagetitle)
{
	if ($(".diffPagePatrolButton").length || $(".diffPagePatrolButtonClicked").length || makeDiffViewRevidsListIsExecuting)
	{
		return;
	}
	makeDiffViewRevidsListIsExecuting = true;
	
	var api = new mw.Api(),
		timestampQueryData = function(id)
		{
			var ret = 
			{
				format: "json",
				formatversion: 2,
				action: "query",
				prop: "revisions",
				rvprop: "timestamp",
				rvstartid: id,
				rvendid: id,
				titles: pagetitle
			};
			return ret;
		};
	
	var leftEditIsPageCreation = ($("#differences-prevlink").length == 0); // czy jest link "przejdź do poprzedniej edycji"
	
	api.post
	(
		timestampQueryData(oldid)
	).done(function(data)
	{
		//debugger;
		var firstTimestamp = data.query.pages[0].revisions[0].timestamp;
		
		//debugger;
		//żeby nie oznaczało jako sprawdzonej pierwszej zmiany (po lewej)
		if (!leftEditIsPageCreation) //ale tylko jeśli była edycja przedtem
		{
			var date = new Date(firstTimestamp);
			date.setSeconds(date.getSeconds() + 1);
			
			function pad2withzeros(number)
			{
				return String(number).padStart(2, "0");
			}
			firstTimestamp = date.getUTCFullYear() + "-" + pad2withzeros(date.getUTCMonth() + 1) + "-" + pad2withzeros(date.getUTCDate()) + "T" + pad2withzeros(date.getUTCHours()) + ":" + pad2withzeros(date.getUTCMinutes()) + ":" + pad2withzeros(date.getUTCSeconds()) + "Z";
		}
		
		api.post
		(
			timestampQueryData(curid)
		).done(function(data)
		{
			//debugger;
			var secondTimestamp = data.query.pages[0].revisions[0].timestamp;
			api.post(
			{
				format: "json",
				formatversion: 2,
				action: "query",
				list: "recentchanges",
				rcstart: firstTimestamp,
				rcend: secondTimestamp,
				rcdir: "newer",
				rctitle: pagetitle,
				rcshow: "!patrolled",
				rcprop: "ids"
			}).done(function(data)
			{
				//debugger;
				var length = data.query.recentchanges.length;
				for (var i = 0; i < length; ++i)
				{
					QuickPatrolDiffPageRevids.push(data.query.recentchanges[i].revid);
				}
				
				if (length > 0)
				{
					makeDiffPagePatrolButton();
				}
				makeDiffViewRevidsListIsExecuting = false;
			}).fail(function(error)
			{
				makeDiffViewRevidsListIsExecuting = false;
			});
		}).fail(function(error)
		{
			makeDiffViewRevidsListIsExecuting = false;
		});
	}).fail(function(error)
	{
		makeDiffViewRevidsListIsExecuting = false;
	});
}

function makeDiffPagePatrolButton()
{
	var patrolButtonCode = "<span class='diffPagePatrolButton'>[<a title='oznacz wszystkie edycje widoczne w porównaniu jako sprawdzone'>oznacz wszystkie edycje jako sprawdzone</a>]</span>";
	if (mw.config.get("skin") != "minerva" && mw.config.get("wgCanonicalSpecialPageName") != "MobileDiff")
	{
		var patrolLink = $("#mw-diff-ntitle4 .patrollink");
		if (patrolLink.length > 0)
		{
			patrolLink.replaceWith(patrolButtonCode);
		}
		else
		{
			$("#mw-diff-ntitle4").append("&nbsp;" + patrolButtonCode);
		}
	}
	else
	{
		//debugger;
		$(".patrollink").append(" • " + patrolButtonCode.replace(/(\[|\])/g, "")); //bez kwadratowych nawiasów
	}
	
	$(".diffPagePatrolButton a").click(function()
	{
		patrolMultipleChangesDiffPage();
	});
}

function patrolMultipleChangesDiffPage()
{
	var deferred = $.Deferred(),
		failureCount = 0,
		successCount = 0,
		length = QuickPatrolDiffPageRevids.length,
		button = $(".diffPagePatrolButton");
	
	//debugger;
	button.off("click").find("a").replaceWith(spinnerCode);
	//debugger;
	
	QuickPatrolDiffPageRevids.forEach(function(oldid)
	{
		QuickPatrolLastClick = 0;
		patrolChange(oldid, true, false)
		.done(function()
		{
			++successCount;
		}).fail(function()
		{
			++failureCount;
		}).always(function()
		{
			if (successCount == length)
			{
				deferred.resolve(successCount, failureCount);
				
				//debugger;
				button.replaceWith("<img src='https://nonsa.pl/images/2/26/Za.svg.png' width='15px' height='15px' />&nbsp;<span style='color: green;' class='diffPagePatrolButtonClicked'>zrobione</span>");
				//debugger;
			}
			else if (successCount + failureCount == length)
			{
				deferred.reject(successCount, failureCount);
				
				button.replaceWith("<img src='https://nonsa.pl/images/a/a8/Przeciw.svg.png' width='15px' height='15px' />&nbsp;<span style='color: red;' class='diffPagePatrolButtonClicked'>błąd</span>");
			}
		});
		//debugger;
	});
	
	//debugger;
	return deferred.promise();
}

function processGroupMarksAndButtons($anchor) //usuwa grupowy wykrzyknik i przycisk patrolowania, jak zajdzie potrzeba
{
	var $parent = $anchor;
	while ($parent.prop("tagName") != "TABLE" && $parent.prop("tagName") != "BODY")
	{
		$parent = $parent.parent();
	}
	
	//wszystkie rozwijalne zmiany/pliki spatrolowane, drugi warunek po to,
	//zeby nie ignorowalo sytuacji, gdy zmiany nie udalo sie spatrolowac
	if ($parent.find(".patrolButton").length == 0 && $parent.find("abbr.unpatrolled").length == 1)
	{
		$parent.find("abbr.unpatrolled").replaceWith("&nbsp;");
		$parent.find(".multiplePatrolButton").replaceWith("<span><img src='https://nonsa.pl/images/2/26/Za.svg.png' width='15px' height='15px' />&nbsp;<span style='color: green;' class='multiplePatrolButtonClicked'>zrobione</span></span>");
	}
}