ResourceHighlight.js

// 
/*
	NOTES:
	
	* make it only apply on mediawiki/user pages that end with a lowercase ".js"
	* un-comment out vars
	* join into a single '(function() {}();' and make everything look tidier
	* fix the html of #source-highlight (sometimes only replaces a character like [&<>] with only the beginning of its entity)
*/
/*
proper condition:
if ([2,8].indexOf(mw.config.get("wgNamespaceNumber")) > -1 && mw.config.get("wgTitle").search(/\.(css|js)$/) > -1) {
	// ... entire code goes here
}
*/
/* interface markup */
$("body").append(
	'<nav id="highlight-popup">\n' +
		'\t\n' +
			'\t\tHighlighted text selected\n' +
			'\t\t\n' +
			'\t\t\n' +
		'\t\n' +
		'\tURL\n' +
		'\t<input type="text" id="highlight-popup-url" />\n' +
		'\tWiki link\n' +
		'\t<input type="text" id="highlight-popup-link" />\n' +
		'\tInterwiki link (no oldid - not recommended)\n' +
		'\t<input type="text" id="highlight-popup-interwiki" />\n' +
		'\tFull selected text\n' +
		'\t<textarea></textarea>\n' +
	'</nav>'
);
mw.util.addCSS(
	'nav#highlight-popup {\n' +
		'\tposition: fixed;\n' +
		'\ttop: ' + ($(window).height() - 332) / 2 + 'px;\n' +
		'\tleft: ' + ($(window).width() - 412) / 2 + 'px;\n' +
		'\tz-index: 100;\n' +
		'\tdisplay: none;\n' +
		'\twidth: 400px;\n' +
		'\theight: 300px;\n' +
		'\tpadding: 5px;\n' +
		'\tbackground: #fafafa;\n' +
		'\tborder: 1px solid #cccccc;\n' +
		'\tborder-radius: 5px;\n' +
	'}\n' +
	'nav#highlight-popup input {\n' +
		'\twidth: 396px;\n' +
		'\tpadding: 0;\n' +
	'}\n' +
	'nav#highlight-popup textarea {\n' +
		'\twidth: 398px;\n' +
		'\theight: 150px;\n' +
		'\tpadding: 0;\n' +
		'\tresize: none;\n' +
	'}\n' +
	'nav#highlight-popup h3 {\n' +
		'\tborder-bottom: 1px solid #cccccc;\n' +
		'\tfont-weight: bold;\n' +
	'}\n' +
	'nav#highlight-popup .highlight-popup-button {\n' +
		'\tfloat: right;\n' +
		'\tdisplay: inline-block;\n' +
		'\twidth: 14px;\n' +
		'\theight: 14px;\n' +
		'\tbackground-position: center;\n' +
		'\tbackground-repeat: no-repeat;\n' +
		'\tcursor: hand;\n' +
		'\tcursor: pointer;\n' +
	'}\n' +
	'nav#highlight-popup #highlight-popup-close {\n' +
		'\tbackground-image: url(\'http://upload.wikimedia.org/wikipedia/commons/thumb/9/9d/Emblem-unreadable.svg/14px-Emblem-unreadable.svg.png\');\n' +
	'}\n' +
	'nav#highlight-popup #highlight-popup-prevent {\n' +
		'\tbackground-image: url(\'http://upload.wikimedia.org/wikipedia/commons/thumb/7/7f/Edit-delete.svg/14px-Edit-delete.svg.png\');\n' +
	'}'
);
$('nav#highlight-popup input[type="text"], nav#highlight-popup textarea').click(function() {
	$(this).select();
});
$("nav#highlight-popup #highlight-popup-close").click(function() {
	$("nav#highlight-popup").hide();
});
$("nav#highlight-popup #highlight-popup-prevent").click(function() {
	$("nav#highlight-popup").attr("data-prevent","true");
});
/* real script */
/*
var parameters = {
	"lineStart": 1,
	"lineEnd": 4
}
*/
var a = $("pre"),
	b = a.clone(),
	b_v2 = a.clone();
b.text(a.text());
b_v2.text(a.text());
b.attr("id", "highlight");
b_v2.attr("id", "highlight_v2");
b.css({
	"position": "absolute",
	"top": a.position().top,
	"left": a.position().left,
	"z-index": "-1",
	"width": a.width(),
	"height": a.height(),
	"color": "transparent"
});
b_v2.css({
	"position": "absolute",
	"top": a.position().top,
	"left": a.position().left,
	"z-index": "1",
	"width": a.width(),
	"height": a.height(),
	"background": "transparent",
	"color": "transparent"
});
a.css("background","transparent");
$("pre").parent().append(b);
$("pre").parent().append(b_v2);
b_v2.scroll(function() {
	a.scrollLeft(b_v2.scrollLeft());
	b.scrollLeft(b_v2.scrollLeft());
});
mw.util.addCSS(
	'#source-highlight {\n' +
		'\tbackground: yellow;\n' +
	'}'
);
// decoding html function - require to move .text() back to .html()
					function decodeLtGt(s) {
						return s.replace(/[<>&]/g, function(a) {
							return a == "<" ? "<" : a == ">" ? ">" : "&";
						});
					}
//location.hash = "#__highlight__;c=8,a=8,b=10,d=15"; // FOR DEBUGGING
location.hash = "#__highlight__;c=3";
(function() {
	/*var*/ c = decodeLtGt(b.text()).split("\n");
	/*var*/ params = {
		a: 1, // starting line
		b: 0, // starting index
		c: c.length, // ending line
		d: c[c.length - 1].length // ending index
	}
	function apply() {
		/*var*/ c = decodeLtGt(b.text()).split("\n");
		// make sure that the hash is for highlighting or something else
		if (location.hash.indexOf("#__highlight__;") == 0) {
			// example syntax: #__highlight__;a=1,b=0,c=40,d=89
			b.html(decodeLtGt(b.text())); // reset highlight
			/*var*/ d = "," + location.hash.split(";")[1] + ",",
				test = { // test parameters to override params
					a: d.match(/,a\=[1-9][0-9]*,/),
					b: d.match(/,b\=(0|[1-9][0-9])*,/),
					c: d.match(/,c\=[1-9][0-9]*,/),
					d: d.match(/,d\=(0|[1-9][0-9])*,/)
				};
			for (/*var*/ i in test) {
				if (isNaN(Number(test[i]))) {
					// result found
					test[i] = Number(test[i][0].split("=")[1].split(",")[0]);
				} else {
					// no result found - value is null - get from params object
					if (i != "d") {
						test[i] = params[i];
					} else {
						// if test.d, match index based on the given 'test.c', and not on the last availabel line, otherwise could result errors
						if (typeof c[test.c-1] === "string") {
							// if valid last line is set, get its last index
							test.d = c[test.c-1].length;
						} else {
							// if no valid line was given, make 'test.d' equal to the length of the last 'c' array item
							test.d = c[c.length-1].length
						}
					}
				}
			}
			function error() {
				console.error(
					"There was an error highlighting some of the page's markup. The passed values were:\n" +
					"- line start (a) : " + test.a + "\n" +
					"- starting index (b) : " + test.b + "\n" +
					"- line start (c) : " + test.c + "\n" +
					"- ending index (d) : " + test.d + "\n" +
					"The values must:\n" +
					"* all values must be integers and cannot be negative\n" +
					"* 'a' must be a positive number\n" +
					"* 'c' must be greater or equal to 'a', and cannot be greater than the number of lines in the page's source\n" +
					"* 'd' cannot be greater than the number of characters in the last passed line\n" +
					"* if 'a' and 'c' are equal, 'b' must not be greater than 'd'"
				);
			}
			/* apply markup in proper place */
			/*
			c[params.lineStart - 1] = '' + c[params.lineStart - 1];
			c[params.lineEnd - 1] += '';
			b.html(c.join("\n"));
			*/
			/*var*/ sameLineBDError = test.a == test.c && test.b > test.d ? true : false; // if a single line highlight, check if test.b < test.d (=error)
			if (
				// integers
				String(test.a).indexOf(".") == -1 &&
				String(test.b).indexOf(".") == -1 &&
				String(test.c).indexOf(".") == -1 &&
				String(test.d).indexOf(".") == -1 &&
				
				// not negative
				test.a > 0 &&
				test.b >= 0 && // can be 0
				test.c > 0 &&
				test.d >= 0 && // can be 0
				
				// last line value not greater than number of lines or less than starting line
				test.c <= c.length &&
				test.c >= test.a &&
				
				// starting index not greater than number of characters in starting line
				test.b <= c[test.a-1].length &&
				
				// make sure that the last line passed by the hash actually exists
				typeof c[test.c-1] === "string" &&
				
				// sameLineBDError is not true
				!sameLineBDError
			) {
				if (
					// last index not greater than number of characters in ending line
					test.d <= c[test.c-1].length
				) {
					// parameters are valid
					console.log("valid has - starting to apply highlight");
					
					if (test.a == test.c) {
						// single line
						c[test.a-1] = decodeLtGt(c[test.a-1].substr(0,test.b)) + '' + decodeLtGt(c[test.a-1].substring(test.b, test.d)) + '' + decodeLtGt(c[test.a-1].substr(test.d));
					} else {
						c[test.a-1] = decodeLtGt(c[test.a-1].substr(0,test.b)) + '' + decodeLtGt(c[test.a-1].substr(test.b));
						c[test.c-1] = decodeLtGt(c[test.c-1].substr(0,test.d)) + '' + decodeLtGt(c[test.c-1].substr(test.d));
					}
					b.html(c.join("\n"));
					$("#source-highlight")[0].scrollIntoView(); // scroll to view
					if ($("nav#highlight-popup").attr("data-prevent") !== "true") {
						var url = 'http://' + location.hostname + '/wiki/' + mw.config.get("wgPageName") + "?oldid=" + mw.config.get("wgCurRevisionId") + location.hash;
						$("nav#highlight-popup #highlight-popup-url").val(url);
						$("nav#highlight-popup #highlight-popup-link").val('[' + url + ' selection]');
						$("nav#highlight-popup #highlight-popup-interwiki").val('[[w:c:' + location.hostname.replace(/\.wikia\.com$/,"") + ':' + mw.config.get("wgPageName") + '|selection]]');
						$("nav#highlight-popup textarea").val('??????');
						$("nav#highlight-popup").show();
					}
				} else {
					// 
					error();
				}
			} else {
				// parameters don't follow the rules
				error();
			}
		}
	}
	$(document).ready(function() {
		apply();
	});
	$(window).on("hashchange", function() {
		apply();
	});
}());
/* get selection part */
$(window).on("keydown", function(k) {
	if (k.keyCode == 36) {
		k.preventDefault();
		/*var*/ selection = window.getSelection();
		if (selection.type == "Range") {
			if ($(selection.baseNode.parentNode).is(b_v2) && $(selection.extentNode.parentNode).is(b_v2)) {
				/*var*/ offset = [selection.baseOffset, selection.extentOffset].sort();
				if (offset[0] > offset[1]) {
					offset.reverse();
				}
				console.log(selection,offset);
				/*var*/ htmlAsText = b_v2.text();
				/*var*/ _0ToStart = htmlAsText.substr(0,offset[0]).split("\n");
				/*var*/ _0ToEnd = htmlAsText.substr(0,offset[1]).split("\n");
				/*var*/ obj = {
					a: _0ToStart.length, // starting line
					b: _0ToStart[_0ToStart.length-1].length, // starting index
					c: _0ToEnd.length, // starting line
					d: _0ToEnd[_0ToEnd.length-1].length, // ending index
				};
				/*var*//* newHtmlAsText = [
					decodeLtGt(htmlAsText.substr(0,offset[0])),
					decodeLtGt(htmlAsText.substring(offset[0], offset[1])),
					decodeLtGt(htmlAsText.substr(offset[1]))
				];
				b.html(newHtmlAsText[0] + '' + newHtmlAsText[1] + '' + newHtmlAsText[2]);
				*/
				location.hash = "#__highlight__;a=" + obj.a + ",b=" + obj.b + ",c=" + obj.c + ",d=" + obj.d;
			}
		}
	}
});
/*
$("body").append(
	'<nav id="highlight-popup">\n' +
		'\t\n' +
			'\t\tHighlighted text selected\n' +
			'\t\t\n' +
		'\t\n' +
		'\tURL\n' +
		'\t<input type="text" id="highlight-popup-url" />\n' +
		'\tWiki link\n' +
		'\t<input type="text" id="highlight-popup-link" />\n' +
		'\tInterwiki link (no oldid - not recommended)\n' +
		'\t<input type="text" id="highlight-popup-interwiki" />\n' +
		'\tFull selected text\n' +
		'\t<textarea></textarea>\n' +
	'</nav>'
);
mw.util.addCSS(
	'nav#highlight-popup {\n' +
		'\tposition: fixed;\n' +
		'\ttop: ' + ($(window).height() - 332) / 2 + 'px;\n' +
		'\tleft: ' + ($(window).width() - 412) / 2 + 'px;\n' +
		'\tz-index: 100;\n' +
		'\tdisplay: none;\n' +
		'\twidth: 400px;\n' +
		'\theight: 300px;\n' +
		'\tpadding: 5px;\n' +
		'\tbackground: #fafafa;\n' +
		'\tborder: 1px solid #cccccc;\n' +
		'\tborder-radius: 5px;\n' +
	'}\n' +
	'nav#highlight-popup input {\n' +
		'\twidth: 396px;\n' +
		'\tpadding: 0;\n' +
	'}\n' +
	'nav#highlight-popup textarea {\n' +
		'\twidth: 398px;\n' +
		'\theight: 150px;\n' +
		'\tpadding: 0;\n' +
		'\tresize: none;\n' +
	'}\n' +
	'nav#highlight-popup h3 {\n' +
		'\tborder-bottom: 1px solid #cccccc;\n' +
		'\tfont-weight: bold;\n' +
	'}\n' +
	'nav#highlight-popup #highlight-popup-close {\n' +
		'\tfloat: right;\n' +
		'\tdisplay: inline-block;\n' +
		'\twidth: 14px;\n' +
		'\theight: 14px;\n' +
		'\tbackground: url(\'http://upload.wikimedia.org/wikipedia/commons/thumb/9/9d/Emblem-unreadable.svg/14px-Emblem-unreadable.svg.png\') center no-repeat;\n' +
		'\tcursor: hand;\n' +
		'\tcursor: pointer;\n' +
	'}'
);
$('nav#highlight-popup input[type="text"], nav#highlight-popup textarea').click(function() {
	$(this).select();
});
$("nav#highlight-popup #highlight-popup-close").click(function() {
	$("nav#highlight-popup").hide();
});
*/
//