User:Guywan/Scripts/BulletSort.js

Note: After saving, you have to bypass your browser's cache to see the changes. Google Chrome, Firefox, Microsoft Edge and Safari: Hold down the ⇧ Shift key and click the Reload toolbar button. For details and instructions about other browsers, see Wikipedia:Bypass your cache.
// [[Category:Wikipedia scripts]]
// <nowiki>
$(function()
{
	if(mw.config.get("wgAction") !== "edit") return;
	
	const debug = true;
	
	// Add key handler.
	window.addEventListener("keyup", e =>
    {
    	if(e.ctrlKey && e.altKey && e.which == 83) run();
	});
	
	function run()
	{
		mw.notify("Sorting...");
		
		// Purely to allow 'Sorting...' to display before we start sorting.
		// That's a 100 miliseconds you'll never get back.
		setTimeout(() =>
		{
			try
			{
				const txtarea = document.getElementById("wpTextbox1");
				const start = txtarea.selectionStart;
				const end = txtarea.selectionEnd;
				
				var lines = txtarea.value.substring(start, end).split("\n");
				var level = "*";
				
				if(debug) console.log(lines);
		
				// (1) Create the tree.
				var t = tree(level, lines)[0];
				
				if(debug) console.log(t);
				
				// (2) Sort the tree.
				new mw.Api().parse(txtarea.value.substring(start, end).replace(/<ref.*?>.*?<\/ref>/g, ""))
				.done((parsed) =>
				{
					parsed = parsed.replace(/(<\/?[^>]*>)|(<!--.*?-->)/g, "").split("\n");
					
					if(debug) console.log(parsed);
					
					var n = [];
					for(var i = 0; i < lines.length; i  )
					{
						if(lines[i].startsWith("*"))
						{
							n.push(lines[i]);
						}
					}
					
					map = {};
					for(i = 0; i < n.length; i  )
					{
						map[n[i]] = parsed[i].trim();
					}
					
					if(debug) console.log(map);
					
					sort(t.sort(compare));
					
					// (3) Convert the tree back into a string and reset txtarea.
					txtarea.value = txtarea.value.substring(0, start)   join(t)   txtarea.value.substr(end   1);
					mw.notify("Done!");
				});
			}
			catch(e)
			{
				console.log(e);
				mw.notify("Failed! See your console for more info.", {type: "error"});
			}
			
		}, 100);
	}
	
	function tree(level, list)
	{
		var branch = [];
		for(var i = 0; i < list.length; i  )
		{
			var line = list[i];
			
			const match = line.match(/^\* /);
			
			if(!match) // No level.
			{
				branch[branch.length - 1].append  = line   "\n";
			}
			else if(match[0] == level) // Same level.
			{
				branch.push({"text": line, "append": "", "children": null});
			}
			else if(match[0] == level   "*") // Lower level.
			{
				var result = tree(level   "*", list.slice(i));
				
				branch[branch.length - 1].children = result[0];
				
				i  = result[1] - 1;
			}
			else // Upper level.
			{
				return [branch, i];
			}
		}
		
		return [branch, i];
	}
		
	function sort(list)
	{
		for(var i = 0; i < list.length; i  )
		{
			if(list[i].children !== null && list[i].children !== undefined)
			{
				list[i].children.sort(compare);
				
				sort(list[i].children);
			}
		}
	}
		
	function join(list)
	{
		var joined = "";
		for(var i = 0; i < list.length; i  )
		{
			joined  = list[i].text   "\n"   list[i].append;
			
			if(list[i].children !== null)
			{
				joined  = join(list[i].children);
			}
		}
		
		return joined;
	}
	
	function compare(a, b)
	{
		return map[a.text].localeCompare(map[b.text]);
	}
});
// </nowiki>