User:Codehydro/Auto CSS image crop/functions.js

From Wikipedia, the free encyclopedia
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.
function automake() {
	if(!imgs[0]) return;
	at = "{{CSS image crop";
	for(i = 0; i < (6 + others.length); i++) at += (cNoBreaks.checked ? "|" : "\r\n| ") + (i < 6 ? labels[i] : others[i - 6][0]) + " = " + (i < 6 ? fe[i].value : others[i - 6][1]);
	autotemp["inner" + (cssNode.styleSheet ? "Text" : "HTML")] = at + (cNoBreaks.checked ? "" : "\r\n") + "}}";
	autotemp.style.height = "";
	autotemp.style.height = autotemp.scrollHeight + 3 + "px";
}

function plusminus1pct(e) {
	if((k = (e ? e : event).keyCode) == 38 || k == 40) {
		(ae = document.activeElement).value = ~~ae.value + Math.max(1, Math.round(lastWidth / 100)) * (k == 38 ? 1 : (k == 40 ? -1 : 0));
		if((ei = Array.prototype.indexOf.call(fe, ae)) === 2 || ei === 3) updateCrop();
		else if(ei > 3) scrollSet();
	}
}

function updateCrop() {
	if(lastWidth && lastWidth == fe[1].value || imgs[0] && ~~imgs[0].max < fe[1].value) {
		if(((rs = fe[ei = 2].value.split(">")).length == 2 || (rs = fe[ei = 3].value.split(">")).length == 2) && ~~rs[1]) {
			rs = (rs[0] || (ei == 2 ? fe[1].value * lastWidthPct : lastHeight * lastHeightPct)) / rs[1] || 1;
			for(i = 5; i > 0; i--) fe[i].value = Math.round((i == ei ? (i == 2 ? fe[1].value * lastWidthPct : lastHeight * lastHeightPct) : fe[i].value) / rs);
			pc.onscroll = "";
			updateImage();
		} else {
			if(!fe[2].value) fe[2].value = Math.round(lastWidth * lastWidthPct);
			if(!fe[3].value) fe[3].value = Math.round(lastHeight * lastHeightPct);
			fe[2].value = Math.max(0, Math.min(parseInt(fe[2].value), lastWidth));
			fe[3].value = Math.max(0, Math.min(parseInt(fe[3].value), lastHeight));
			pc.style.width = (parseInt(fe[2].value) + scrBar) + "px";
			pc.style.height = (parseInt(fe[3].value) + scrBar) + "px";
			scrollSet();
			drawClick();
			pc.onscroll = scrollFind;
		}
		cform.className = "";
	}
}

function lastPct() {
	if(lastWidth && lastWidth == fe[1].value) {
		lastTopPct = pc.scrollTop / lastHeight;
		lastLeftPct = pc.scrollLeft / lastWidth;
		lastWidthPct = fe[2].value / lastWidth;
		lastHeightPct = fe[3].value / lastHeight;
		sizeinfo.innerHTML = "Image data: " + Math.round(size / 102.4) / 10 + " KB | Non-visible overhead: " + Math.round((nvPct = 1 - lastWidthPct * lastHeightPct) * 100) + "%, ~ " + Math.round((nonvis = size * (nvPct)) / 102.4) / 10 + " KB";
		if(mainKB && nonvis > mainKB * (big = 4)) {
			alert("Uploading a cropped image could reduce data by about " + Math.round(nonvis / 102.4) / 10 + "kB versus using this template. I am programmed to suggest this or a reduction in bSize when non-visible data is over " + Math.round(mainKB * big / 102.4) / 10 + "kB = " + big + "x the HTML data of '/wiki/Main Page' as of " + loadtime);
			mainKB = 0;
		}
	}
	automake();
}

function scrollFind() {
	if(lastWidth && lastWidth == fe[1].value) {
		pc.scrollTop = Math.max(0, Math.min(lastHeight - fe[3].value, pc.scrollTop));
		pc.scrollLeft = Math.max(0, Math.min(lastWidth - fe[2].value, pc.scrollLeft));
		pc.scrollTop = fe[4].value = Math.round(pc.scrollTop);
		pc.scrollLeft = fe[5].value = Math.round(pc.scrollLeft);
		lastPct();
	}
	drawClick();
}

function scrollSet() {
	if(lastWidth && lastWidth == fe[1].value) {
		fe[4].value = Math.max(0, Math.min(lastHeight - fe[3].value, parseInt(fe[4].value)));
		fe[5].value = Math.max(0, Math.min(lastWidth - fe[2].value, parseInt(fe[5].value)));
		pc.scrollTop = fe[4].value;
		pc.scrollLeft = fe[5].value;
		lastPct();
	}
}

function copyCrop() {
	i = 0;
	if(lastClick[1])
		while(i < 4 && mc[i].innerHTML == fe[i++ +2].value);
	if(lastFile && i < 4 && (fe[4].value || fe[5].value) > 0 && confirm("Draw lines around current visible area on next image? (Adds 2 points to click history; set with '" + cb1.innerText + "' on form.)")) return(lastClick[0] ? [] : lastClick).unshift([fe[5].value / lastWidth, fe[4].value / lastHeight], [(~~fe[5].value + ~~fe[2].value) / lastWidth, (~~fe[4].value + ~~fe[3].value) / lastHeight]);
	return;
}

function updateImage() {
	if(fe[6].getAttribute("readonly")) {
		setTimeout(updateImage, 99);
		return;
	}
	if(lastFile && imgs[0]) lastFile = imgs[0].alt.replace("File:", "");
	fe[0].value = fe[0].value.replace("File:", "").trim();
	if(!~fe[1].value.indexOf("max")) fe[1].value = parseInt(fe[1].value) || lastWidth;
	else if(imgs[0]) fe[1].value = imgs[0].max;
	if(fe[0].value && fe[1].value && (lastFile != fe[0].value || lastWidth != fe[1].value)) {
		if((isSame = lastFile == fe[0].value)) {
			fe[2].value = Math.round(fe[1].value * lastWidthPct);
			fe[3].value = Math.round((nh = (fe[1].value * lastHeight / lastWidth)) * lastHeightPct);
			fe[4].value = Math.round(nh * lastTopPct);
			fe[5].value = Math.round(fe[1].value * lastLeftPct);
			if(lastClick[0]) drawClick();
		} else {
			if(window.imp2 && imp2[0] != fe[0].value) others = [];
			load = new XMLHttpRequest();
			load.open('GET', '/wiki/File:' + fe[0].value, false);
			load.send();
			(ajx = document.createElement('div')).innerHTML = load.responseText.replace(/( src\=)/g, " srcurl=");
			for(i = (imgtag = ajx.getElementsByTagName("img")).length - 1; ~i; i--)
				if(imgtag[i].alt.indexOf("File:")) imgtag[i].parentNode.removeChild(imgtag[i]);
			imgtag = ajx.getElementsByTagName("img");
			if(imgtag[0]) {
				copyCrop();
				imgs.unshift(imgtag[0]);
			} else {
				fe[0].value = (iv = "INVALID: ") + fe[0].value.replace(iv, '');
				fe[0].onclick = function() {
					fe[0].value = fe[0].value.replace(iv, fe[0].onclick = '');
				};
				return;
			}
		}
		imgs[0].max = imgs[0].getAttribute("data-file-width");
		if(~fe[1].value.indexOf("max")) fe[1].value = imgs[0].max;
		if((wd = imgs[0].max - fe[1].value) < 0) {
			alert((isSame ? "Last valid crop restored." : "bSize set to max allowed.") + " Wiki servers will not process bSizes greater than the original image width" + (isSame ? " (" + imgs[0].max + "px). Type 'max' in bSize field to set." : ""));
			if(isSame) {
				fe[2].value += ">" + Math.round(lastWidth * lastWidthPct);
				return updateCrop();
			}
			fe[1].value = imgs[0].max;
			wd = 0;
		}
		srcurl = imgs[0].getAttribute("srcurl").replace(RegExp(imgs[0].width + "(?=px)"), fe[1].value);
		if(!wd) srcurl = srcurl.replace(RegExp("/(thumb|" + fe[1].value + "[\\w\\W]+)", "g"), "");
		else if(srcurl.indexOf("thumb") < 0) srcurl = srcurl.replace(RegExp("(./../)(?=" + (fn = imgs[0].alt.replace("File:", "").replace(/ /g, "_")) + ")"), "thumb/$1") + "/" + fe[1].value + "px-" + fn;
		if(~(ua = window.navigator.userAgent).indexOf("MSIE ") && ua.substr(ua.indexOf("MSIE ") + 5, 2) < 10) {
			(load = new XDomainRequest()).open('GET', srcurl);
			load.send();
			load.onload = function() {
				size = load.responseText.replace(/[\u0000-\u007F]/g, 9).replace(/[\u0080-\u07ff]/g, 99).replace(/[\u0800-\uffd7]/g, 999).length - (load.responseText.substr(1, 3) == "PNG" ? 8 : 4);
			};
		} else {
			load.open('HEAD', srcurl, false);
			load.send();
			size = load.getResponseHeader("content-length");
		}
		cform.className = "hide";
		imgcrop.src = srcurl;
	} else setTimeout(updateCrop, 9);
}

function cImport() {
	if(!fe[6].value || fe[6].getAttribute("readonly")) return;
	impSlow = false;
	if(!others[0] || confirm("The following will be discarded unless you cancel:\n " + others[0][0] + autotemp.innerHTML.split(others[0][0])[1].slice(0, -2).replace(/<[Bb][Rr]>/g, "").replace(/\r?\n?\| ?/g, "\n "))) {
		imp = fe[6].value.split("|");
		(imp2 = []).count = 0;
		o2 = others;
		others = [];
		trimReg = /(^[ \t\r\n]+|(px)?[} \t\r\n]+$)/g;
		for(i = 1; imp[i]; i++) {
			params = imp[i].split("=");
			if((pli = labels.indexOf(params[0].trim())) >= 0) {
				imp2.count++;
				imp2[pli] = pli > 0 ? parseInt(params[1]) : params[1].replace(trimReg, "");
			} else if(params.length == 2) {
				(ot = []).push(params[0].replace(trimReg, ""));
				ot.push(params[1].replace(trimReg, ""));
				others.push(ot);
			}
		}
		if(imp2.count == labels.length) {
			fe[6].value = "Import successful";
			for(n = 0; n < imp2.length; n++) {
				fe[n].value = imp2[n];
				if(n == 1) updateImage();
			}
			cform.className = "hide";
		} else {
			others = o2;
			impSlow = true;
			fe[6].value = "Error, missing:";
			for(i = 0; labels[i]; i++)
				if(!imp2[i]) fe[6].value += " " + labels[i];
		}
	} else fe[6].value = "Cancelling";
	fe[6].setAttribute("readonly", 1);
	imp = "";
	setTimeout(function() {
		fe[6].value = '';
		fe[6].removeAttribute("readonly");
		cform.className = '';
	}, impSlow ? 3e3 : 500);
}

function loadCheck() {
	if(imgcrop.complete) {
		pc.onclick = findClick;
		pc.className = "";
		if(window.gl)
			for(i = 0; gl[i]; i++) pc.appendChild(gl[i]);
		if(lastWidth === "")
			for(i = 2; i < 6; i++) fe[i].onkeyup = plusminus1pct;
		if(!mainKB) {
			(mainKB = new XMLHttpRequest()).open('HEAD', '/wiki', false);
			mainKB.send();
			mainKB = mainKB.getResponseHeader("content-length");
			loadtime = new Date();
		}
		lastWidth = pc.firstChild.offsetWidth - scrBar;
		lastHeight = pc.firstChild.offsetHeight - scrBar;
		if(!fe[6].value && lastFile != fe[0].value) unCrop();
		lastFile = fe[0].value = imgs[0].alt.replace("File:", "");
		setTimeout(updateCrop, 9);
	} else setTimeout(loadCheck, 99);
}

function findClick(e) {
	if((e = e || window.event).shiftKey) undoClick();
	else {
		mouse = {
			x: e.clientX || e.pageX,
			y: e.clientY || e.pageY
		};
		pc.x = pc.y = 0;
		for(xy = pc; xy; xy = xy.offsetParent) {
			pc.x += xy.offsetLeft;
			pc.y += xy.offsetTop;
		}
		lastClick.unshift([(mouse.x - pc.x + (window.pageXOffset || document.documentElement.scrollLeft) + pc.scrollLeft) / lastWidth, (mouse.y - pc.y + (window.pageYOffset || document.documentElement.scrollTop) + pc.scrollTop) / lastHeight]);
	}
	drawClick();
}

function undoClick() {
	lastClick.shift();
	if(lastClick[0]) drawClick();
	else clearClick();
}

function scaleClick(index, op) {
	tO = [typeof "", typeof 1, typeof 0[0], typeof !0].indexOf(typeof op);
	return Math.round(Math[!tO ? op : "min"](lastClick[tO == 1 ? op : 0][index], lastClick[tO == 1 ? op : (tO == 3 ? 0 : 1)][index]) * (index ? lastHeight : lastWidth));
}

function glAnim() {
	if(mc) {
		for(ga = 0; ga < 4; ga++) gl[ga].style.borderStyle = gl[ga].style.borderStyle == "dashed" ? "dotted" : "dashed";
		setTimeout(glAnim, 99);
	}
}

function drawClick() {
	if(lastClick[0]) {
		if(!mc) {
			mc = [];
			gl = [];
			for(i = 0; i < 4; i++) {
				rows[i + 2].appendChild(mc[i] = document.createElement("td"));
				(gl[i] = document.createElement("div")).style.cssText = "border-width:" + (i < 2 ? "1px 0" : "0 1px") + ";border-color:rgba(0,0,0,0.7);border-" + (b2 = i < 2 ? "top" : "left") + "-color:#fff;border-" + b2 + "-color:rgba(255,255,255,0.7);";
				pc.appendChild(gl[i]).className = "gl";
			}
			glAnim();
		}
		if(lastClick.length == 1) {
			oCoord = [
				[scaleClick(0, 0), scaleClick(1, 0)]
			];
			for(i = 0; mc[i]; i++) mc[i].innerHTML = "";
		} else {
			oCoord = [
				[scaleClick(0), scaleClick(1)],
				[scaleClick(0, "max"), scaleClick(1, "max")]
			];
			for(i = 0; i < 2; i++) {
				mc[i].innerHTML = Math.abs(oCoord[1][i] - oCoord[0][i]);
				mc[i + 2].innerHTML = oCoord[0][1 - i];
			}
		}
		clickInfo.innerHTML = "Click history: " + oCoord[0].join(", ") + (oCoord[1] ? ";  " + scaleClick(0, 1) + ", " + scaleClick(1, 1) + (lastClick[2] ? " (+" + (lastClick.length - 2) + " more)" : "") : "");
		for(i = 0; i < 2; i++) {
			if(oCoord[i]) {
				gl[i].style.left = pc.offsetLeft + "px";
				gl[i + 2].style.top = pc.offsetTop + "px";
				gl[i].style.top = (oCoord[i][1] + pc.offsetTop - pc.scrollTop) + "px";
				gl[i + 2].style.left = (oCoord[i][0] + pc.offsetLeft - pc.scrollLeft) + "px";
			}
			gl[i].style.width = (oCoord[i] && oCoord[i][1] - fe[4].value < fe[3].value && oCoord[i][1] > fe[4].value ? fe[2].value : 0) + "px";
			gl[i + 2].style.height = (oCoord[i] && oCoord[i][0] - fe[5].value < fe[2].value && oCoord[i][0] > fe[5].value ? fe[3].value : 0) + "px";
		}
	}
}

function clearClick() {
	if(mc && rows[2].cells[2]) {
		for(i = 0; i < 4; i++) {
			rows[i + 2].removeChild(mc[i]);
			pc.removeChild(gl[i]);
		}
		gl = mc = 0;
		clickInfo.innerHTML = "Click history cleared.";
	}
	lastClick = [];
}

function cropClick() {
	if(mc && mc[0].innerHTML) {
		for(i = 0; i < 4; i++) fe[i + 2].value = mc[i].innerHTML;
		updateCrop();
	} else alert("No area selected.");
}

function unCrop() {
	fe[2].value = fe[1].value = lastWidth;
	fe[3].value = lastHeight;
	fe[4].value = fe[5].value = lastTopPct = lastLeftPct = 0;
	lastWidthPct = lastHeightPct = 1;
	if(lastFile == fe[0].value) setTimeout(updateCrop, 9);
}

function initAutoCrop() {
	document.body.appendChild(outer = document.createElement("div")).style.width = "100px";
	widthNoScroll = outer.offsetWidth;
	outer.style.overflow = "scroll";
	(inner = document.createElement("div")).style.width = "100%";
	outer.appendChild(inner);
	scrBar = widthNoScroll - inner.offsetWidth;
	outer.parentNode.removeChild(outer);
	lastFile = lastWidth = lastHeight = "";
	labels = ["Image", "bSize", "cWidth", "cHeight", "oTop", "oLeft"];
	others = [], lastClick = [], imgs = [];
	autocrop.innerHTML = "<form id='cform'><table><tr><td>Image</td><td><input onblur='updateImage()'></td><td id='clickInfo'>Click image to draw guide lines.</td></tr><tr><td>bSize</td><td><input onblur='updateImage()' /></td><td><div class='cButton' onclick='clearClick()'>Clear history</div></td></tr><tr><td>cWidth</td><td><input onblur='updateCrop()' /></td></tr><tr><td>cHeight</td><td><input onblur='updateCrop()' /></td></tr><tr><td>oTop</td><td><input onblur='scrollSet()' /></td></tr><tr><td>oLeft</td><td><input onblur='scrollSet()' /></td></tr><tr class='break'><td>Import</td><td><textarea onblur='cImport()'></textarea></td></tr></table></form><div id=sizeinfo></div><div id='pc' class='hide'><div><img id='imgcrop'/></div></div><p><div class='cButton' onclick='cropClick()' id='cb1'>Crop to clicked area</div><div class='cButton' onclick='unCrop()'>Uncrop image</div>Suppress output newlines?<input type='checkbox' id='cNoBreaks' onclick='automake()' /></p><textarea readonly id='autotemp'></textarea>";
	(cssNode = (document.head || document.getElementsByTagName("head")[0]).appendChild(document.createElement("style"))).type = "text/css";
	(cssNode.styleSheet || cssNode)[cssNode.styleSheet ? "cssText" : "innerHTML"] = "#pc{overflow:scroll;}#cform input{width:16em;}#pc.hide,.hide input{visibility:hidden;}#cform textarea{width:100%;height:3em;}#cform table{border-spacing:0;}tr.break>td{border-top:2px solid #000;}[readonly],.cButton{background-color:#eee;}.cButton{cursor:pointer;outline:1px solid;text-align:center;vertical-align:middle;margin: 0 0.3em;padding: 0 0.5em;}#pc >:first-child{padding-bottom:" + scrBar + "px;padding-right:" + scrBar + "px;}.cButton,#pc>:first-child,.gl{display:inline-block;}.gl{position:absolute;z-index:9;border-style:dotted;height:0;width:0;}";
	fe = cform.elements;
	rows = cform.firstChild.rows;
	mainKB = mc = 0;
	rows[1].cells[2].innerHTML = cb1.outerHTML.replace(1, 2) + rows[1].cells[2].innerHTML;
	if(!window.editform) window.onbeforeunload = function() {
		if(lastWidth != fe[2].value || lastHeight != fe[3].value || window.gl) return "Unsaved work may be lost if you leave this page.";
	};
	imgcrop.onload = loadCheck;
	imgcrop.onerror = function() {
		imgs.shift();
		alert("Download failed. Try again later.");
		cform.className = "";
		updateImage();
	};
	autotemp.onclick = function() {
		autotemp.setSelectionRange(0, autotemp.value.length);
	};
}