User:Ricordisamoa/FlagSense/unstable.js

Note: After saving, you have to bypass your browser's cache to see the changes. Internet Explorer: press Ctrl-F5, Mozilla: hold down Shift while clicking Reload (or press Ctrl-Shift-R), Opera/Konqueror: press F5, Safari: hold down Shift + Alt while clicking Reload, Chrome: hold down Shift while clicking Reload.
/* <nowiki>
 * FlagSense by Ricordisamoa
 * http://commons.wikimedia.org/wiki/User:Ricordisamoa/FlagSense
*/

FlagSense={
	
	messages:{
		"add-template-VVA":{
			"en":"Add {{Vector version available}} to the file description page",
			"it":"Aggiungi {{Vector version available}} alla pagina di descrizione del file"
		},
		"add-template-toSVG":{
			"en":"Add {{Convert to SVG|flag}} to the file description page",
			"it":"Aggiungi {{Convert to SVG|flag}} alla pagina di descrizione del file"
		},
		"add-template-toPNG":{
			"en":"Add {{Convert to PNG}} to the file description page",
			"it":"Aggiungi {{Convert to PNG}} alla pagina di descrizione del file"
		},
		"add-template-ssPNG":{
			"en":"Add {{SupersededPNG}} to the file description page",
			"it":"Aggiungi {{SupersededPNG}} alla pagina di descrizione del file"
		},
		"feature-not-available":{
			"en":"Add {{SupersededPNG}} to the file description page",
			"it":"Aggiungi {{SupersededPNG}} alla pagina di descrizione del file"
		},
		"alreadySVG":{
			"en":"This is already a vector image.\nIt doesn't need to be converted in SVG again.",
			"it":"Questa è già una immagine vettoriale.\nNon necessita di essere convertita in SVG di nuovo."
		},
		"vvaConfirmToSVG":{
			"en":"This page already contains the template {{Vector version available}}\n"+
				"To replace {{Vector version available}} with {{Convert to SVG}}, press OK;\n"+
				"To skip editing this page, press UNDO",
			"it":"Questa pagina contiene già il template {{Vector version available}}\n"+
				"Per sostituire {{Vector version available}} con {{Convert to SVG}}, premi OK;\n"+
				"Per evitare di modificare questa pagina, premi ANNULLA"
		},
		"vvaConfirmToPNG":{
			"en":"This page already contains the template {{Vector version available}}\n"+
				"To replace {{Vector version available}} with {{Convert to PNG}}, press OK;\n"+
				"To skip editing this page, press UNDO",
			"it":"Questa pagina contiene già il template {{Vector version available}}\n"+
				"Per sostituire {{Vector version available}} con {{Convert to PNG}}, premi OK;\n"+
				"Per evitare di modificare questa pagina, premi ANNULLA"
		},
		"vvaConfirmNew":{
			"en":"This page already contains the template {{Vector version available}}\n"+
				"To replace the existing istance with this new one, press OK;\n"+
				"To skip editing this page, press UNDO",
			"it":"Questa pagina contiene già il template {{Vector version available}}\n"+
				"Per sostituire la vecchia istanza con quella nuova, premi OK;\n"+
				"Per evitare di modificare questa pagina, premi ANNULLA"
		},
		"svgAlternative":{
			"en":"The name of the SVG alternative (without File: prefix):",
			"it":"Il nome dell'alternativa SVG (senza prefisso File:)"
		},
		"pngAlternative":{
			"en":"The name of the PNG alternative (without File: prefix):",
			"it":"Il nome dell'alternativa PNG (senza prefisso File:)"
		},
		"alreadySameCategory":{
			"en":function(width,height,cat){return "FlagSense detected that, by a size of "+width+"×"+height+", the proper category for this image was: «"+cat+"».\n"+
					"However, the image is already in this category."},
			"it":function(width,height,cat){return "FlagSense ha rilevato che, con una dimensione di "+width+"×"+height+", la categoria appropriata per questa immagine sarebbe stata: «"+cat+"».\n"+
					"Tuttavia, l'immagine è già in questa categoria."}
		},
		"alreadyOtherCategory":{
			"en":function(width,height,cat){return "FlagSense detected that, by a size of "+width+"×"+height+", the proper category for this image would be: «"+cat+"».\n"+
					"However, the image is already categorized by aspect ratio.\nDo you anyway want to replace the old category with this one?"},
			"it":function(width,height,cat){return "FlagSense ha rilevato che, con una dimensione di "+width+"×"+height+", la categoria appropriata per questa immagine sarebbe: «"+cat+"».\n"+
					"Tuttavia, l'immagine è già categorizzata per proporzioni.\nVuoi comunque sostituire la vecchia categoria con questa?"}
		},
		"noMatchFound":{
			"en":function(width,height){return "FlagSense tried to search the proper category for this image, but no matches were found.\n\n"+
					"Details:\nimage size: "+width+"×"+height+"\naspect ratio: "+width/FlagSense.gcd(width,height)+"/"+height/FlagSense.gcd(width,height)},
			"it":function(width,height){return "FlagSense ha provato a cercate la categoria appropriata per questa immagine, ma non ha trovato corrispondenze.\n\n"+
					"Dettagli:\ndimensioni immagine: "+width+"×"+height+"\nproporzioni: "+width/FlagSense.gcd(width,height)+"/"+height/FlagSense.gcd(width,height)}
		}
	},
	
	start:function(){
		// Make sure the utilities module is loaded (will only load if not already)
		mw.loader.using( "mediawiki.util", function () {
			if(wgCanonicalNamespace=="File"){// Make sure the page is a file description
	
				// Wait for the page to be parsed
				$(document).ready( function () {
			
					wgFileExtension=wgPageName.split('.')[wgPageName.split('.').length-1].toLowerCase();
					// parse the file extension from the page name
					// the variable is global, so can be used later for automatic categorization
					
					var $_GET=[]; // object which contains key/value pairs of the querystring
					if(location.search&&location.search.toString().length>1){ // the querystring is present
						for(s in location.search.split("?")[1].split("&")){ // split the pairs
							var key=location.search.split("?")[1].split("&")[s].split("="); // split the key
							$_GET[decodeURIComponent(key[0])]=decodeURIComponent(key[1]);   // from the value
						}
					}
					
					if(document.getElementById("editform")&&((sessionStorage&&sessionStorage["FlagSense-currentAction"])||$_GET["flagsense"])){
						// the user is editing the page and the current page was previously marked for editing
						
						var f = document.editform; // the edit form
						var t = f.wpTextbox1;      // the edit textarea
						var s = f.wpSummary;       // the summary of changes
						var m = f.wpMinoredit;     // the checkbox for marking a minor edit
						
						switch($_GET["flagsense"]?$_GET["flagsense"]:sessionStorage["FlagSense-currentAction"]){
							
							// the user is going to mark this image as to be converted to SVG
							case "toSVG":(function(){
								var template="{{Convert to SVG|flag}}";  // no comment!
								
								if(FlagSense.regex["VVA"].test(t.value)==false){
									t.value=template+"\n"+t.value; // add the template code at the top
								}
								else if(confirm(FlagSense.getMessage("vvaConfirmToSVG"))==true){ // ask for a confirmation
									t.value=t.value.replace(FlagSense.regex["VVA"],template); // replace "Vector version available" with "Convert to SVG";
								}
								else{
									window.history.back(); // go back to the file description page
								}
								s.value="Added template {{[[Template:Convert to SVG|Convert to SVG]]|flag}} using [[User:Ricordisamoa/FlagSense|FlagSense]]"; // precompiled summary
								FlagSense.closeEdit();
							})();break;
							
							
							// the user is going to mark this image as to be converted to PNG
							case "toPNG":(function(){
								var template="{{Convert to PNG}}";  // no comment!
								
								if(FlagSense.regex["VVA"].test(t.value)==false){
									t.value=template+"\n"+t.value; // add the template code at the top
								}
								else if(confirm(FlagSense.getMessage("vvaConfirmToPNG"))==true){ // ask for a confirmation
									t.value=t.value.replace(FlagSense.regex["VVA"],template); // replace "Vector version available" with "Convert to SVG";
								}
								else{
									window.history.back(); // go back to the file description page
								}
								s.value="Added template {{[[Template:Convert to PNG|Convert to PNG]]}} using [[User:Ricordisamoa/FlagSense|FlagSense]]"; // precompiled summary
								FlagSense.closeEdit();
							})();break;
							
							
							// the user is going to point out a vector version of this image
							case "VVA":(function(){
								var dial=$("<div>");
								var input=$("<input>").appendTo(dial);
								dial.dialog({title:FlagSense.getMessage("svgAlternative"),buttons:{
									"OK":function(){
										var template="{{Vector version available|"+input.val()+"}}";
										if(FlagSense.regex["VVA"].test(t.value)==false){
											t.value=template+"\n"+t.value; // add the template code at the top
										}
										else if(confirm(FlagSense.getMessage("vvaConfirmNew"))==true){ // ask for a confirmation
											t.value=t.value.replace(FlagSense.regex["VVA"],template); // replace the old "Vector version available" with the new one;
										}
										else{
											window.history.back(); // go back to the file description page
										}
										s.value="Added template {{[[Template:Vector version available|Vector version available]]}} using [[User:Ricordisamoa/FlagSense|FlagSense]]"; // precompiled summary
										$(this).dialog("close");
										FlagSense.closeEdit();
									},
									"Cancel":function(){$(this).dialog("close");}
								}});
							})();break;
							
							
							// the user is going to point out a PNG version of this image
							case "ssPNG":(function(){
								var dial=$("<div>");
								var input=$("<input>").appendTo(dial);
								dial.dialog({title:FlagSense.getMessage("pngAlternative"),buttons:{
									"OK":function(){
										var template="{{SupersededPNG|"+input.val()+"}}";
										if(FlagSense.regex["VVA"].test(t.value)==false){
											t.value=template+"\n"+t.value; // add the template code at the top
										}
										else if(confirm(FlagSense.getMessage("vvaConfirmToPNG"))==true){ // ask for a confirmation
											t.value=t.value.replace(FlagSense.regex["VVA"],template); // replace the old "Vector version available" with the new one;
										}
										else{
											window.history.back(); // go back to the file description page
										}
										s.value="Added template {{[[Template:SupersededPNG|SupersededPNG]]}} using [[User:Ricordisamoa/FlagSense|FlagSense]]"; // precompiled summary
										$(this).dialog("close");
										FlagSense.closeEdit();
									},
									"Cancel":function(){$(this).dialog("close");}
								}});
							})();break;
							
							
							case "cat":(function(){
								var cat=sessionStorage?sessionStorage["FlagSense-category"]:$_GET["cat"]; // category from sessionStorage or querystring
								var accuracy=parseFloat(sessionStorage?sessionStorage["FlagSense-accuracy"]:$_GET["accuracy"]); // category from sessionStorage or querystring
								var oldCatTest=/\[\[Category:[^\[\]]*Flag[^\[\]]+aspect ratio[^\[\]]+\]\]/gi;
								if(oldCatTest.test(t.value)){
									oldCatTest.lastIndex=0;
									s.value="removed [[:Category:"+oldCatTest.exec(t.value)[0].toString().replace(/\[\[Category:/i,"")+"; "; // precompiled summary
									t.value=t.value.replace(oldCatTest,"[[Category:"+cat+"]]"); // replace the old category with the new one
								}
								else t.value+=(/\n$/.test(t.value)?"":"\n")+"[[Category:"+cat+"]]"; // add the category at the bottom
								s.value+="added [[:Category:"+cat+"]] using [[User:Ricordisamoa/FlagSense|FlagSense]]"+(accuracy>0?" with an error of ±"+accuracy:""); // precompiled summary
								m.checked=true;
								FlagSense.closeEdit();
							})();break;
							
						}
					}
					
					else{
						
						var width=$(".filehistory>tbody>tr:nth-child(2)>td:nth-child(4)")[0].firstChild.nodeValue.split(' ').join('').split('\u00A0').join('').split('×');
						var height=parseInt(width[1]);
						width=parseInt(width[0]);
						var g=FlagSense.gcd(width,height); // greatest common divisor of width and height
						var ratio=width/g+":"+height/g;
						var cat=FlagSense.categoryByAspectRatio(width,height);

						$("div#file").prepend(
							$(document.createElement("div"))
							.css({float:"right",background:"whitesmoke",padding:"8px"})
							.append("<h3>FlagSense</h3>")
							.append("Format: "+wgFileExtension.toUpperCase())
							.append(
								(wgFileExtension!="svg"?// check for non-SVG images
								$(document.createElement("p"))
								.attr("id","FlagSense-nonSVG")
								.append($("<button>VVA</button>")
									.attr("id","FlagSense-button-VVA")
									.attr("title",FlagSense.getMessage("add-template-VVA"))
									.click(function(event){
										FlagSense.openEdit("VVA");
									}))
								:""))
							.append(
								(wgFileExtension!="png"?// check for non-PNG images
								$(document.createElement("p"))
								.attr("id","FlagSense-nonPNG")
								.append($("<button>SupersededPNG</button>")
									.attr("id","FlagSense-button-ssPNG")
									.attr("title",FlagSense.getMessage("add-template-ssPNG"))
									.click(function(event){
										FlagSense.openEdit("ssPNG");
									}))
								:""))
							.append($(new Image()).attr(
								(cat&&cat!=null&&wgCategories.indexOf(cat.cat)!=-1)?{
									"src":"//upload.wikimedia.org/wikipedia/commons/thumb/b/be/P_yes.svg/20px-P_yes.svg.png",
									"title":"already in this category"
								}:(cat==null?{
									"src":"//upload.wikimedia.org/wikipedia/commons/thumb/4/42/P_no.svg/20px-P_no.svg.png",
									"title":"no proper category available for this image"
								}:{
									"src":"//upload.wikimedia.org/wikipedia/commons/thumb/5/5e/Exclamation.svg/20px-Exclamation.svg.png",
									"title":"a category has been found"+(FlagSense.isCategorizedByAspectRatio()==true?" but the image is already categorized by aspect ratio":"")
								})
							).css("background","none"))// remove the default checkered background
							.append("Aspect ratio: ")
							.append(cat!=null?$("<a>"+cat.ratio+"</a>").attr("href",mw.util.getUrl("Category:"+cat.cat)):ratio)
							.append(cat!=null?(" with"+(cat.accuracy>0?" an error of ±"+Math.round(cat.accuracy*100000)/100000:"out error")):"")
							.append(
								(cat!=null&&wgCategories.indexOf(cat.cat)==-1)?$("<button>"+(FlagSense.isCategorizedByAspectRatio()==true?"Change category":"Add category")+"</button>")
								.click(function(event){
									FlagSense.openEdit("cat",cat.cat,Math.round(cat.accuracy*100000)/100000);
								})
							:"")
							.append($("<br/><button>Categorize by elements and colors<br/>(experimental)</button>")
								.click(function(event){
									// create an HTML5 Canvas
									var canvas=$(document.createElement("canvas"))
									.attr({"width":$("div#file a>img").attr("width"),"height":$("div#file a>img").attr("height")})
									.css("background",$("div#file a>img").css("background"));
									var ctx=canvas[0].getContext("2d");
									var imageObj=new Image();
									imageObj.crossOrigin="Anonymous";// allow CORS images
									imageObj.onload=function(){
										ctx.drawImage(imageObj,0,0);// draw the image on the canvas
										canvas.click(function(event){
											var obj=this;
											var top=0;
											var left=0;
											while(obj!=document.body) {
												top+=obj.offsetTop;
												left+=obj.offsetLeft;
												obj=obj.offsetParent;
											}
											var x=event.clientX-left+window.pageXOffset;
											var y=event.clientY-top+window.pageYOffset;
											var imageData=this.getContext("2d").getImageData(x,y,1,1).data;
											var red=imageData[0];
											var green=imageData[1];
											var blue=imageData[2];
											var alpha=imageData[3];
											alert("x: "+x+"\ny: "+y+"\nrgba("+[red,green,blue,alpha].join()+")\n"+FlagSense.colorByRGBA(red,green,blue,alpha));
										});
										var imageColors=FlagSense.colorsByData(canvas.get(0).getContext("2d").getImageData(0,0,canvas.get(0).width,canvas.get(0).height).data);
										console.log(JSON.stringify(imageColors));
										$.each(imageColors,function(index,element){
											var li=$(document.createElement("li")).text(element.shift());
											$.each(element,function(i,e){
												li.append(
													$(document.createElement("span"))
													.css({
														"display":"inline-block",
														"width":"12pt",
														"height":"12pt",
														"background":e
													})
												);
											});
											$("ul#FlagSense-colors").append(li);
										});
									}
									imageObj.src=$("div#file a>img").attr("src");// get the URL of the in-page thumbnail
									$("div#file a>img").parent().replaceWith(canvas);// replace the in-page thumb (and the link)
									$(this).replaceWith($(document.createElement("ul")).attr("id","FlagSense-colors"));// remove the button
								})
							)
						);
						
						if($("p#FlagSense-nonSVG").length>0){
							FlagSense.isTemplated(FlagSense.regex["toSVG"],function(result){
								if(result===false){
									$("p#FlagSense-nonSVG").prepend($("<button>Should be SVG</button>")
									.attr("id","FlagSense-button-toSVG")
									.attr("title",FlagSense.getMessage("add-template-toSVG"))
									.click(function(event){
										FlagSense.openEdit("toSVG");
									}))
								}
							});
							
							FlagSense.isTemplated(FlagSense.regex["VVA"],function(result){
								if(result===true){
									$("button#FlagSense-button-VVA")
									.attr("title",$("button#FlagSense-button-VVA").attr("title")+" (change)")
									.append(" (change)")
								}
							});
						}
						
						if($("p#FlagSense-nonPNG").length>0){
							FlagSense.isTemplated(FlagSense.regex["toPNG"],function(result){
								if(result===false){
									$("p#FlagSense-nonPNG").prepend($("<button>Should be PNG</button>")
									.attr("id","FlagSense-button-toPNG")
									.attr("title",FlagSense.getMessage("add-template-toPNG"))
									.click(function(event){
										FlagSense.openEdit("toPNG");
									}))
								}
							});
							
							FlagSense.isTemplated(FlagSense.regex["ssPNG"],function(result){
								if(result===true){
									$("button#FlagSense-button-ssPNG")
									.attr("title",$("button#FlagSense-button-ssPNG").attr("title")+" (change)")
									.append(" (change)")
								}
							});
						}
						
					}
			
				});
			}
		});
	},
	
	colorsByData:function(data){
		var colors=[];
		for(i=0;i<data.length;i+=4){
			var red=data[i];
			var green=data[i+1];
			var blue=data[i+2];
			var alpha=data[i+3];
			var color=FlagSense.colorByRGBA(red,green,blue,alpha);
			var rgba="rgba("+[red,green,blue,alpha].join()+")";
			if(color!=null){
				$.each(colors,function(index,element){
					if(element.shift()==color&&element.indexOf(rgba)==-1) element.push(rgba);
					else if(index==colors.length-1) colors.push([color,rgba]);
				});
				if(colors.length==0) colors.push([color,rgba]);
			}
		}
		return colors;
	},
	
	colorByRGBA:function(red,green,blue,alpha){
		if(alpha==255){
			if(red>green&&red>blue) return "red";
			else if(green>red&&green>blue) return "green";
			else if(blue>red&&blue>green) return "blue";
			else if(red==green&&red==blue){
				if(red==255) return "white";
				else if(red==0) return "black";
				else return "gray";
			}
		}
		else if(alpha==0) return "transparent";
		else return null;
	},

	categoryByAspectRatio:function(width,height){
		// see http://commons.wikimedia.org/wiki/Category:Flags_by_aspect_ratio
		var ratios=["1:1","2:1","3:2","4:3","5:3","18:11","18:13","20:11","25:14","8:5"];
		
		var g=FlagSense.gcd(width,height); // greatest common divisor of width and height
		var ratio=width/g+":"+height/g;
		var accuracy=0; // accuracy is the difference between the flag's aspect ratio and the closest existing category
		var paragorns=[];
		for(r in ratios){
			var rs=ratios[r].split(':');
			var diff=Math.abs(parseInt(rs[0])/parseInt(rs[1])-width/height); // absolute difference between the two ratios
			console.log("FlagSense: examining ratio: "+rs.join("/")+"; difference is: "+diff);
			if(diff<.04) paragorns.push([ratios[r],diff]);
		}
		if(paragorns.length>0){            // sort all ratios whose difference is lower than 0.04
	                                   	// the first is the most appropriate
			console.log("FlagSense: unordered ratios: "+paragorns);
			paragorn=paragorns.sort(this.sortRatios)[0];
			console.log("FlagSense: most appropriate ratio: "+paragorn);
			var cat="Flags with an aspect ratio of "+paragorn[0]; // proper category by aspect ratio
			var ratio=paragorn[0];
			var accuracy=paragorn[1];
			if(ratio=="1:1") cat="Square flags";
			if(wgFileExtension=="svg"){
				if(ratio=="1:1") cat="SVG square flags";
				else cat=cat.replace(/^Flags/,"SVG flags").replace(/an (aspect)/,"$1");
			}
			if(ratio=="3:2"){
				if(wgFileExtension=="png") cat="PNG flags - aspect ratio of 3:2";
				else if(wgFileExtension=="gif") cat="GIF flags with aspect ratio of 3:2";
			}
			return {
				ratio:ratio,
				cat:cat,
				accuracy:accuracy
			};
		}
		else{
			return null;
		}
	},
	
	sortRatios:function(a,b){
		return a[1]-b[1]; // sort two aspect ratios basing on the accuracy level
	},
	
	isTemplated:function(regex,callback){
		$.get(wgServer+"/w/index.php?title="+wgPageName+"&action=raw",function(data){
			callback(regex.test(data));
		});
	},
	
	gcd:function(a,b){ // recursive function for Greatest Common Divisor
		if(b) return this.gcd(b,a%b);
		else return Math.abs(a);
	},
	
	getMessage:function(message){
		return FlagSense.messages[message][wgUserLanguage];
	},
	
	isCategorizedByAspectRatio:function(){
		$.each(wgCategories,function(index,cat){// loop in all categories of the image
			if(cat.indexOf("flag")!=-1&&cat.indexOf("aspect ratio")!=-1) return true;// there is a category by aspect ratio
		});
		return false;// there aren't
	},
	
	openEdit:function(action,category,accuracy){
		if(sessionStorage){
			sessionStorage["FlagSense-currentAction"]=action;// mark this page for adding the template (just later)
			if(category!=null&&accuracy!=null){
				sessionStorage["FlagSense-category"]=category;
				sessionStorage["FlagSense-accuracy"]=accuracy;
			}
		}
		document.location=$("li#ca-edit a").attr("href")+(!sessionStorage?"&flagsense="+action+((category&&accuracy&&category!=null&&accuracy!=null)?"&cat="+category+"&accuracy="+accuracy:""):""); // go to edit page
	},

	closeEdit:function(){
		if(sessionStorage){
			sessionStorage.removeItem("FlagSense-currentAction"); // unmark the page
			sessionStorage.removeItem("FlagSense-category");
			sessionStorage.removeItem("FlagSense-accuracy");
		}
		if(this.autoSubmit&&this.autoSubmit==true) document.editform.submit();   // automatically submit the edit form
	},
	
	regex:{
		"VVA":/\{\{(Vector Version Available|Converted to SVG|SVG available|NowSVG|VVA)\|[^\n\{\}]+\}\}/i,
		"toSVG":/\{\{([Cc]onvert( to |[Tt]o)|[Ss]hould( be |[Bb]e)|[Tt]o)?SVG(\|[a-zA-Z]+)?\}\}/,
		"toPNG":/\{\{([Cc]onvert to |[Ss]hould( be |[Bb]e))PNG\}\}/,
		"ssPNG":/\{\{[Ss]upersededPNG\|[^\n\{\}]+\}\}/
	},
	
	autoSubmit:false

};
FlagSense.start();
/* </nowiki> */