Artex / FA Justified Gallery

// ==UserScript==
// @name        FA Justified Gallery
// @namespace   Artex
// @description sorts galleries into a justified grid
// @include     https://www.furaffinity.net/*
// @include     http://www.furaffinity.net/*
// @exclude     https://www.furaffinity.net/view/*
// @exclude     http://www.furaffinity.net/view/*
// @version     1.0.2
// @grant       none
// ==/UserScript==

//TODO: write cases for story, music, and flash thumbnails.
//	for stories always display title. 
// 	if feeling motivated provide description preview on thumb.
var SIZE = 300; //thumbnail height - any number you please. If it's higher then RES thumbnails may be blurred.
var RES = 400; //thumbnail resolution - I haven't done a thorough check, but I know 150, 200, 300, and 400 are valid resolutions
var flexImages=function(){function e(e){function t(e,r,n,i){function o(e){n.maxRows&&g>n.maxRows||n.truncate&&e&&g>1?c[a][0].style.display="none":(c[a][4]&&(c[a][3].setAttribute("src",c[a][4]),c[a][4]=""),c[a][0].style.width=l+"px",c[a][0].style.height=h+"px",c[a][0].style.display="block")}for(var a,l,s,d,f=1,g=1,u=e.clientWidth-2,c=[],m=0,h=n.rowHeight,w=0;w<r.length;w++)if(c.push(r[w]),m+=r[w][2]+n.margin,m>=u){var p=c.length*n.margin;for(f=(u-p)/(m-p),h=Math.ceil(n.rowHeight*f),s=0,l,a=0;a<c.length;a++)l=Math.ceil(c[a][2]*f),s+=l+n.margin,s>u&&(l-=s-u),o();c=[],m=0,g++}for(a=0;a<c.length;a++)l=Math.floor(c[a][2]*f),d=Math.floor(n.rowHeight*f),o(!0);i||u==e.clientWidth||t(e,r,n,!0)}if(document.querySelector){var r={selector:0,container:".item",object:"img",rowHeight:180,maxRows:0,truncate:0};for(var n in e)e.hasOwnProperty(n)&&(r[n]=e[n]);for(var i="object"==typeof r.selector?[r.selector]:document.querySelectorAll(r.selector),o=0;o<i.length;o++){var a=i[o],l=a.querySelectorAll(r.container),s=[],d=(new Date).getTime();if(l.length){var f=window.getComputedStyle?getComputedStyle(l[0],null):l[0].currentStyle;r.margin=(parseInt(f.marginLeft)||0)+(parseInt(f.marginRight)||0)+(Math.round(parseFloat(f.borderLeftWidth))||0)+(Math.round(parseFloat(f.borderRightWidth))||0);for(var g=0;g<l.length;g++){var u=l[g],c=parseInt(u.getAttribute("data-w")),m=c*(r.rowHeight/parseInt(u.getAttribute("data-h"))),h=u.querySelector(r.object);s.push([u,c,m,h,h.getAttribute("data-src")])}t(a,s,r);var w=function(){t(a,s,r)};document.addEventListener?(window["flexImages_listener"+d]=w,window.removeEventListener("resize",window["flexImages_listener"+a.getAttribute("data-flex-t")]),delete window["flexImages_listener"+a.getAttribute("data-flex-t")],window.addEventListener("resize",window["flexImages_listener"+d])):a.onresize=w,a.setAttribute("data-flex-t",d)}}}}return e}();!function(){"function"==typeof define&&define.amd?define("flexImages",function(){return flexImages}):"undefined"!=typeof module&&module.exports?module.exports=flexImages:window.flexImages=flexImages}();

function injectCSS() {
	var style = [
		"center.flow u { display: unset; }",
		".frontpage { max-height: unset !important; }",
		".item a { border: none !important;}",
		".r-mature { outline: 1px solid #697cc1 !important;}",
		".r-adult { outline: 1px solid #971c1c !important; }",
		".checked { opacity: 0.6;}",
		".checked:hover { opacity: 1 }",
		".item span { background-color: rgba(0,0,0,0.5); position: absolute; bottom: 0; left: 0; width: 100%; height: 35px;}",
		"center.flow s { display: unset; }",
		"center.flow s > a { width: 100%; height: 100%;}",
		//FLEX-IMAGES CSS
		".flex-images { overflow: hidden; }",
		".flex-images .item { float: left; margin: 4px; box-sizing: content-box; overflow: hidden; position: relative;  }",
		".flex-images .item img { display: block; width: 100%; height: 100%; }",
	];
	var styleEl = document.createElement("style");
	styleEl.innerHTML = style.join("");
	document.head.appendChild(styleEl);
}

function moveTitle(thumb) {
	let title = thumb.getElementsByTagName("span")[0];
	if (title) {//home page doesn't have titles
		let artist = thumb.getElementsByTagName("small");
		artist = artist[artist.length - 1]; //quick work around to fix artists titles in notification center
		let a = thumb.getElementsByTagName("a")[0]; 
		var i = thumb.getElementsByTagName("i")[0]; //description icon

		//these don't always exist
		if (i) { 
			a.removeChild(i);
		}
		if (artist) {
			title.appendChild(artist);
		}

		title.style.display = "none";
		a.appendChild(title);

		thumb.addEventListener("mouseenter", function() {
			title.style.display = "unset"; //switch to css classes for any further modifications
		});
		thumb.addEventListener("mouseleave", function() {
			title.style.display = "none";
		});	
	}
}

function upscaleThumbs(gallery) {
	let thumbs = gallery.querySelectorAll(".r-general, .r-mature, .r-adult");
	for(var i = 0; i < thumbs.length; i++) {
		let img = thumbs[i].getElementsByTagName("img")[0];
		img.src = img.src.replace(/@\d+/,"@" + RES);

		thumbs[i].style.maxWidth = "unset";
		thumbs[i].style.maxHeight = "unset";
		thumbs[i].style.width = "unset";
		//flex-images
		thumbs[i].setAttribute("data-w", img.width == 0 ? SIZE : img.width *  (SIZE/200));
		thumbs[i].setAttribute("data-h", img.height == 0 ? SIZE : img.height *  (SIZE/200));
		img.style.maxWidth = "unset";
		img.width = "100%";
		img.height = "100%";
		thumbs[i].classList.add("item");
		//deal with titles
		moveTitle(thumbs[i]);
	}
	gallery.classList.add("flex-images"); 
	new flexImages({ selector: gallery, rowHeight: SIZE});
}

window.addEventListener("DOMContentLoaded", function() {
	var galleries = document.getElementsByClassName("flow");
	if (galleries) {
		injectCSS();
		for (var i = 0; i < galleries.length; i++) {
			upscaleThumbs(galleries[i]);
		}
	}
});