Sleazy Fork is available in English.

[Konachan / yande.re / LB] Avatar: Storage

Lets you save a list of avatars in your browser's local storage for later use. Save avatars from the "Set avatar" page. Avatars are accessible from the "Profile" and "Set Avatar" pages. Drag avatars to reorder. Click on avatars for options.

// ==UserScript==
// @name		[Konachan / yande.re / LB] Avatar: Storage
// @namespace	Zolxys
// @description	Lets you save a list of avatars in your browser's local storage for later use. Save avatars from the "Set avatar" page. Avatars are accessible from the "Profile" and "Set Avatar" pages. Drag avatars to reorder. Click on avatars for options.
// @include		/^https?://konachan\.com/(user/(show|set_avatar)/\d+($|\?|#)|images/blank\.gif\#Zolxys_AvatarStorage)/
// @include		/^https?://konachan\.net/(user/(show|set_avatar)/\d+($|\?|#)|images/blank\.gif\#Zolxys_AvatarStorage)/
// @include		/^https?://yande\.re/user/(show|set_avatar)/\d+($|\?|#)/
// @include		/^https?://lolibooru\.moe/user/(show|set_avatar)/\d+($|\?|#)/
// @version		1.4
// ==/UserScript==
spacing = 3;
upwardList = false;

function parse(s, b = false) {
	try {
		var a = JSON.parse(s);
		if (!Array.isArray(a))
			throw '';
	}
	catch (e) {
		if (b)
			alert('Invalid JSON data found in localStorage.Zolxys_AvatarStorage');
		return null;
	}
	for (var i = a.length - 1; i >= 0; --i)
		if (typeof a[i] != 'object')
			a.splice(i,1);
	return a;
}
function avatarList() {
	return (localStorage.Zolxys_AvatarStorage)? parse(localStorage.Zolxys_AvatarStorage, true) : [];
}
function openMenu(e) {
	if (e.ctrlKey || e.altKey || e.shiftKey)
		return;
	e.stopPropagation();
	e.preventDefault();
	var o = document.getElementById('zol_AvatarStorage_Menu');
	if (o.visible() && e.button != 2 && document.getElementById('zol_AvatarStorage_Remove').getAttribute('zol_id') == this.id)
		o.style.display = 'none';
	else {
		document.getElementById('zol_AvatarStorage_Set').href = this.parentNode.href;
		document.getElementById('zol_AvatarStorage_Remove').setAttribute('zol_id', this.id);
		o.style.display = 'inline-block';
		document.getElementById('zol_AvatarStorage_Import').focus();
		o.style.marginLeft = 0;
		o.style.marginLeft = (e.clientX - o.getBoundingClientRect().x - Math.max(e.clientX + o.getBoundingClientRect().width - document.body.clientWidth, 0)) +'px';
		o.style.marginTop = 0;
		o.style.marginTop = (e.clientY - o.getBoundingClientRect().y - Math.max(e.clientY + o.getBoundingClientRect().height - innerHeight, 0)) +'px';
	}
	document.getElementById('zol_AvatarStorage').style.display = 'inline-block';
}
function checkAvatar(o) {
	try {
		var r = /^(?:https?:)?\/\/[^/]+(\/.+)/.exec(o.img);
		if (r)
			o.img = r[1];
		r = /^(\/.+\/)[^/]+(\.[^./]+)($|\?|#)/.exec(o.img);
		if (r)
			o.img = r[1] +'z'+ r[2];
		if (!isNaN(o.id) && !isNaN(o.x) && !isNaN(o.y) && !isNaN(o.x2) && !isNaN(o.y2) && !isNaN(o.w) && !isNaN(o.h) && o.img)
			return {id: o.id, x: o.x, y: o.y, x2: o.x2, y2: o.y2, w: o.w, h: o.h, img: o.img};
	} catch (e) {}
	return null;
}
function addAvatar(add, a) {
	for (var i = 0; i < a.length; ++i)
		if (a[i].id == add.id && a[i].x == add.x && a[i].y == add.y && a[i].x2 == add.x2 && a[i].y2 == add.y2)
			return a;
	if (add = checkAvatar(add))
		a.push(add);
	return a;
}
function save(a, ts = null) {
	localStorage.Zolxys_AvatarStorage = JSON.stringify(a);
	if (kona) {
		localStorage.Zolxys_AvatarStorage_ts = (tsm[0] & 15) +' '+ (lts = ((ts == null)? Date.now() : ts));
		if (ts == null)
			mrg = [[[], 0]];
		for (var i = fa.length - 1; i >= 0; --i)
			fa[i][1].contentWindow.postMessage(['Zolxys_AvatarStorage', localStorage.Zolxys_AvatarStorage_ts, localStorage.Zolxys_AvatarStorage], fa[i][0]);
	}
	var o = document.getElementById('zol_AvatarStorage');
	if (o.visible()) {
		o.style.display = 'none';
		document.getElementById('zol_AvatarStorage_Menu').style.display = 'none';
		document.getElementById('zol_AvatarStorage_Link').click();
	}
}
function importAvatars(e) {
	e.stopPropagation();
	e.preventDefault();
	var a, aa;
	var s = e.clipboardData.getData('text');
	try {
		a = (s)? JSON.parse(s) : null;
	}
	catch (e) {
		alert('Invalid JSON data found in pasted input');
		return;
	}
	if (!a || !(aa = avatarList()))
		return;
	var i = 0;
	var r = null, x;
	if (typeof a[0] == 'string') {
		r = /(?:^|\.)(([^.]+)\.[^.]+)$/;
		x = r.exec(location.hostname);
		r = r.exec(a[0]);
		if (!r || !x || r[2] != x[2]) {
			alert('The pasted input is labeled for use on '+ a[0]);
			return;
		}
		++i;
	}
	for (; i < a.length; ++i)
		aa = addAvatar(a[i], aa);
	save(aa);
}
function newStoragePopupLink(before) {
	var ne = document.createElement('a');
	ne.id = 'zol_AvatarStorage_Link';
	ne.href = '#';
	ne.textContent = 'Avatar storage';
	ne.addEventListener('click', function (e) { // Main Link EL: Open List
		e.stopPropagation();
		e.preventDefault();
		if (document.getElementById('zol_AvatarStorage').visible()) {
			document.getElementById('zol_AvatarStorage').style.display = 'none';
			document.getElementById('zol_AvatarStorage_Menu').style.display = 'none';
		}
		else {
			var a = document.getElementById('zol_AvatarStorage').childNodes;
			for (var i = a.length-1; i >= 0; --i)
				a[i].parentNode.removeChild(a[i]);
			var o = document.getElementById('zol_AvatarStorage');
			if (!(a = avatarList()))
				return;
			var r = /^\/user\/show\/(\d+)/.exec(location.pathname);
			if (!r)
				r = /[&?]user_id=(\d+)(&|$)/.exec(location.search);
			var uid = (r)? '&user_id='+ r[1] : '';
			var col = Math.floor((innerHeight - 10 + spacing) / (125 + spacing));
			if (!a.length) {
				var ne = document.createElement('span');
				ne.textContent = 'There are no saved avatars for this domain: '+ location.hostname;
				o.appendChild(ne);
				o.appendChild(document.createElement('br'));
				o.appendChild(document.createElement('hr'));
				o.appendChild(document.createTextNode('Import from text:'));
				o.appendChild(document.createElement('br'));
				ne = o.appendChild(document.createElement('input'));
				ne.id = 'zol_AvatarStorage_Import';
				ne.addEventListener('paste', importAvatars);
				o.style.width = '';
				o.style.height = '';
				o.style.display = 'inline-block';
				ne.focus();
			}
			else {
				for (var i = 0; i < a.length; ++i) {
					var w = (a[i].x2 - a[i].x) * a[i].w;
					var h = (a[i].y2 - a[i].y) * a[i].h;
					var sc = Math.min(125/w, 125/h);
					var ne = document.createElement('img');
					ne.style.backgroundColor = '#222';
					ne.src = a[i].img;
					ne.style.width = Math.round(a[i].w * sc) +'px';
					ne.style.height = Math.round(a[i].h * sc) +'px';
					ne.style.left = Math.round(a[i].x * a[i].w * sc * -1) +'px';
					ne.style.top = Math.round(a[i].y * a[i].h * sc * -1) +'px';
					ne.setAttribute('zol_id', a[i].id);
					ne.setAttribute('zol_x', a[i].x);
					ne.setAttribute('zol_y', a[i].y);
					ne.setAttribute('zol_x2', a[i].x2);
					ne.setAttribute('zol_y2', a[i].y2);
					ne.setAttribute('zol_w', a[i].w);
					ne.setAttribute('zol_h', a[i].h);
					ne.setAttribute('zol_img', a[i].img);
					ne.id = 'zol_AvatarStorage_Img'+ i;
					ne.addEventListener('click', openMenu);
					ne.addEventListener('contextmenu', openMenu);
					ne.addEventListener('dragover', function (e) { // Image Drag-Over EL
						if (/^zol_AvatarStorage_Img\d+$/.test(this.id))
							e.preventDefault();
					});
					ne.addEventListener('drop', function (e) { // Image Drop EL
						e.preventDefault();
						var o = document.getElementById(e.dataTransfer.getData('text')); // Warning: dataTransfer will be null if an alert is used prior to this
						var fr = null, to = null;
						var a = avatarList();
						if (!a)
							return;
						var d = {
							id: o.getAttribute('zol_id'),
						 	x: o.getAttribute('zol_x'),
						 	y: o.getAttribute('zol_y'),
						 	x2: o.getAttribute('zol_x2'),
						 	y2: o.getAttribute('zol_y2'),
							w: o.getAttribute('zol_w'),
							h: o.getAttribute('zol_h'),
							img: o.getAttribute('zol_img')
						};
						for (var i = 0; i < a.length; ++i)
						 if (a[i].id == d.id && a[i].x == d.x && a[i].y == d.y && a[i].x2 == d.x2 && a[i].y2 == d.y2 && a[i].w == d.w && a[i].h == d.h && a[i].img == d.img) {
						 	fr = i;
							break;
						}
						d = {
							id: this.getAttribute('zol_id'),
						 	x: this.getAttribute('zol_x'),
						 	y: this.getAttribute('zol_y'),
						 	x2: this.getAttribute('zol_x2'),
						 	y2: this.getAttribute('zol_y2'),
							w: this.getAttribute('zol_w'),
							h: this.getAttribute('zol_h'),
							img: this.getAttribute('zol_img')
						};
						for (var i = 0; i < a.length; ++i)
						 if (a[i].id == d.id && a[i].x == d.x && a[i].y == d.y && a[i].x2 == d.x2 && a[i].y2 == d.y2 && a[i].w == d.w && a[i].h == d.h && a[i].img == d.img) {
						 	to = i;
							break;
						}
						if (fr != null && to != null) {
							a.splice(to, 0, a.splice(fr, 1)[0]);
							save(a);
						}
					});
					var ee = ne;
					ne = document.createElement('a');
					ne.appendChild(ee);
					ne.href = location.protocol +'//'+ location.host +'/user/set_avatar/'+ a[i].id +'?x='+ a[i].x * a[i].w +'&y='+ a[i].y * a[i].h +'&x2='+ a[i].x2 * a[i].w +'&y2='+ a[i].y2 * a[i].h + uid;
					ne.addEventListener('dragstart', function (e) { // Image (<a>) Drag EL
						e.dataTransfer.setData("text", this.getElementsByTagName('img')[0].id);
					});
					ee = ne;
					ne = document.createElement('div');
					ne.appendChild(ee);
					ne.className = 'imgCrop_previewWrap';
					ne.style.width = Math.round(w * sc) +'px';
					ne.style.height = Math.round(h * sc) +'px';
					ne.style.position = 'absolute';
					ne.style.marginTop = ((125 + spacing) * (i%col)) +'px';
					ne.style.marginLeft = ((125 + spacing) * Math.floor(i/col)) +'px';
					o.appendChild(ne);
				}
				o.style.width = ((125 + spacing) * Math.ceil(a.length / col) - spacing) +'px';
				o.style.height = ((125 + spacing) * Math.min(col, a.length) - spacing) +'px';
			}
			o = document.getElementById('zol_AvatarStorage');
			o.style.display = 'inline-block';
			o.style.marginTop = 0;
			if (upwardList)
				o.style.marginTop = (-1 * Math.min(o.getBoundingClientRect().height, o.getBoundingClientRect().y)) +'px';
			else
				o.style.marginTop = Math.min(innerHeight - o.getBoundingClientRect().bottom, 0) +'px';
			o.style.marginLeft = 0;
			o.style.marginLeft = Math.max(Math.min(document.body.clientWidth - o.getBoundingClientRect().right, 0), 10 - o.getBoundingClientRect().x) +'px';
		}
	});
	before.parentNode.insertBefore(ne, before);
	ne = document.createElement('ul');
	ne.id = 'zol_AvatarStorage';
	ne.className = 'submenu';
	ne.style.background = 'none repeat scroll 0 0 black';
	ne.style.border = '1px solid #666';
	ne.style.display = 'none';
	ne.style.margin = '0';
	ne.style.padding = '4px';
	ne.style.position = 'absolute';
	ne.style.textAlign = 'left';
	ne.style.whiteSpace = 'pre';
	ne.style.zIndex = '1000';
	ne.addEventListener('click', function (e) {
		e.stopPropagation();
	});
	before.parentNode.insertBefore(ne, before);
	ne = document.createElement('ul');
	ne.id = 'zol_AvatarStorage_Menu';
	ne.className = 'submenu';
	ne.style.background = 'none repeat scroll 0 0 black';
	ne.style.border = '1px solid #666';
	ne.style.display = 'none';
	ne.style.margin = '0';
	ne.style.padding = '3px 4px 5px';
	ne.style.position = 'absolute';
	ne.style.textAlign = 'left';
	ne.style.whiteSpace = 'nowrap';
	ne.style.zIndex = '1001';
	ne.addEventListener('click', function (e) {
		e.stopPropagation();
	});
	before.parentNode.insertBefore(ne, before);
	document.addEventListener('click', function (e) { // This is only really needed for yande.re (2017-01-21)
		if (e.button)
			return;
		document.getElementById('zol_AvatarStorage').style.display = 'none';
		document.getElementById('zol_AvatarStorage_Menu').style.display = 'none';
	});
	var o = ne;
	ne = o.appendChild(document.createElement('a'));
	ne.textContent = 'Set avatar';
	ne.id = 'zol_AvatarStorage_Set';
	o.appendChild(document.createElement('br'));
	ne = o.appendChild(document.createElement('a'));
	ne.textContent = 'Remove avatar';
	ne.id = 'zol_AvatarStorage_Remove';
	ne.href = '#';
	ne.addEventListener('click', function (e) { // Remove EL
		e.stopPropagation();
		e.preventDefault();
		if (confirm('Are you sure you want to remove this avatar?')) {
			var a = avatarList();
			if (!a)
				return;
			for (var i = 0; i < a.length; ++i) {
				var id = document.getElementById(this.getAttribute('zol_id'));
				if (a[i].id == id.getAttribute('zol_id')
				 	&& a[i].x == id.getAttribute('zol_x')
				 	&& a[i].y == id.getAttribute('zol_y')
				 	&& a[i].x2 == id.getAttribute('zol_x2')
				 	&& a[i].y2 == id.getAttribute('zol_y2')
					&& a[i].w == id.getAttribute('zol_w')
					&& a[i].h == id.getAttribute('zol_h')
					&& a[i].img == id.getAttribute('zol_img')
				) {
				 	a.splice(i, 1);
					save(a);
					break;
				}
			}
		}
	});
	o.appendChild(document.createElement('br'));
	o.appendChild(document.createElement('hr'));
	ne = o.appendChild(document.createElement('a'));
	ne.textContent = 'Export list to clipboard';
	ne.href = '#';
	ne.addEventListener('click', function (e) { // Export EL
		e.stopPropagation();
		e.preventDefault();
		var a = avatarList();
		if (!a)
			return;
		a.unshift(location.hostname);
		var ne = document.createElement('textarea');
		ne.textContent = JSON.stringify(a);
		document.body.appendChild(ne);
		ne.select();
		document.execCommand('copy');
		document.body.removeChild(ne);
	});
	o.appendChild(document.createElement('br'));
	o.appendChild(document.createTextNode('Import from text:'));
	o.appendChild(document.createElement('br'));
	ne = o.appendChild(document.createElement('input'));
	ne.id = 'zol_AvatarStorage_Import';
	ne.addEventListener('paste', importAvatars);
}
function checkRemote(s, b = false) {
	var sa = parse(s, b);
	if (!sa)
		return null;
	var a = [];
	var o;
	for (var i = 0; i < sa.length; ++i)
		if (o = checkAvatar(sa[i]))
			a.push(o);
	return a;
}
function mergeData(aa, a) {
	if (!a)
		return aa;
	for (var i = 0; i < a.length; ++i)
		addAvatar(a[i], aa);
	return aa;
}
function recieve(e) {
	var r = /^http(s?):\/\/konachan\.(com|net)$/;
	var x = r.exec(e.origin);
	if (!kona || !Array.isArray(e.data) || e.data[0] != 'Zolxys_AvatarStorage' || !x)
		return;
	if (sub) {
		localStorage.Zolxys_AvatarStorage = e.data[2];
		localStorage.Zolxys_AvatarStorage_ts = e.data[1];
		return;
	}
	var o = document.getElementById('zol_AvatarStorage_'+ e.origin);
	if (!o)
		return;
	var a = checkRemote((localStorage.Zolxys_AvatarStorage || '[]'), true);
	if (!a)
		return;
	x = [x, r.exec(location.protocol +'//'+ location.host)];
	if (!e.data[1])
		e.data[1] = '0 0';
	var ts = (Number(e.data[1].substr(2)) || 0);
	lts = (localStorage.Zolxys_AvatarStorage_ts || '0 0');
	tsm = [
		((Number(e.data[1].substr(0,2)) || 0) | (((x[0][1])? 2 : 1) * ((x[0][2] == 'com')? 4 : 1))) & 15,
		((Number(	lts.substr(0,2))	|| 0) | (((x[1][1])? 2 : 1) * ((x[1][2] == 'com')? 4 : 1))) & 15,
	];
	tsm[0] |= tsm[1];
	lts = (Number(lts.substr(2)) || 0);
	var i = fa.length - 1;
	while (i >= 0)
		if (fa[i--][1] == o)
			break;
	if (i == -1)
		fa.push([e.origin, o]);
	var rec = localStorage.Zolxys_AvatarStorage;
	if (lts == 0)
		mrg.push(rec);
	if (ts == 0 && tsm[0] != tsm[1])
		mrg.push(e.data[2]);
	else if (ts < lts) {
		localStorage.Zolxys_AvatarStorage_ts = tsm[0] +' '+ lts;
		o.contentWindow.postMessage(['Zolxys_AvatarStorage', localStorage.Zolxys_AvatarStorage_ts, JSON.stringify(a)], e.origin);
		return;
	}
	else if (localStorage.Zolxys_AvatarStorage != e.data[2] && parse(e.data[2])) {
		rec = e.data[2];
		mrg[0][1] = 0;
	}
	a = checkRemote(rec);
	if (!a) {
		rec = '[]';
		a = [];
	}
	ts = (ts || 1);
	for (var i = mrg[0][1] + 1; i < mrg.length; ++i) {
		mrg[0][1] = i;
		if (!mrg[i] || mrg[i] == '[]' || mrg[i] == rec || i != mrg.indexOf(mrg[i]))
			continue;
		mrg[0][0] = mergeData(mrg[0][0], checkRemote(mrg[i]));
	}
	a = mergeData(a, mrg[0][0]);
	save(a, ts);
}
function init() {
	if (!kona || fa)
		return;
	fa = [];
	for (var i = 0; i < 4; ++i) {
		var p = ((i & 1)? 'https:' : 'http:');
		var d = ((i & 2)? '.com' : '.net');
		if (location.protocol == p && location.hostname.substr(-4) == d)
			continue;
		var ne = document.body.appendChild(document.createElement('iframe'));
		ne.style.display = 'none';
		ne.id = 'zol_AvatarStorage_'+ p +'//konachan'+ d;
		ne.name = location.protocol +'//'+ location.host;
		ne.src = p +'//konachan'+ d +'/images/blank.gif#Zolxys_AvatarStorage';
	}
}
function replaceCropper(x1, y1, x2, y2) {
	document.getElementsByClassName('avatar-crop')[0].appendChild(document.getElementById('image'));
	var o = document.getElementsByClassName('imgCrop_wrap')[0]; o.parentNode.removeChild(o);
	var options =
	{
		displayOnInit: true,
		onEndCrop: onEndCrop,
		previewWrap: 'crop-preview',
		minWidth: 1,
		minHeight: 1,
		onloadCoords: {x1: x1, y1: y1, x2: x2, y2: y2},
		resizePreview: function(dim)
		{
			var sc = Math.min(125/dim.x, 125/dim.y);
			return {x: Math.round(dim.x * sc), y: Math.round(dim.y * sc)};
		}
	}
	new Cropper.ImgWithPreview('image', options);
}
var kona = /^konachan\.(com|net)$/.test(location.hostname);
var sub = false;
var fa = null;
var mrg = [[[], 0]];
var lts = (localStorage.Zolxys_AvatarStorage_ts || '0 0');
var tsm = [(Number(lts.substr(0,2)) || 0)];
lts = (Number(lts.substr(2)) || 0);
if (location.pathname == '/images/blank.gif') {
	sub = true;
	window.addEventListener('message', function(e) {
		recieve(e);
	});
	var ne = document.body.appendChild(document.createElement('iframe'));
	parent.postMessage(['Zolxys_AvatarStorage', localStorage.Zolxys_AvatarStorage_ts, localStorage.Zolxys_AvatarStorage], name);
}
else {
	var a = avatarList();
	if (!a)
		return;
	window.addEventListener('message', function(e) {
		recieve(e);
	});
	init();
	var r = /^\/user\/show\/(\d+)/.exec(location.pathname);
	if (r) {
		var b = /\sBan\s/.test(document.getElementById('subnavbar').textContent);
		if (!b) {
			var x = /\/user\/show\/(\d+)/.exec(document.getElementById('main-menu').getElementsByClassName('user')[0].getElementsByTagName('li')[0].getElementsByTagName('a')[0].href);
			b = (r[1] == x[1]);
		}
		if (b)
			newStoragePopupLink(document.getElementById('content').getElementsByTagName('table')[0]);
	}
	else if (/^\/user\/set_avatar\//.test(location.pathname)) {
		var o = document.getElementById('crop-preview-box');
		var ne = document.createElement('a');
		ne.href = '#';
		ne.textContent = 'Save in storage';
		ne.addEventListener('click', function (e) { // Save EL
			e.stopPropagation();
			e.preventDefault();
			var o = document.getElementById('image');
			var r = /^\/user\/set_avatar\/(\d+)/.exec(location.pathname);
			var id = r[1];
			var x = document.getElementById('left').value;
			var y = document.getElementById('top').value;
			var x2 = document.getElementById('right').value;
			var y2 = document.getElementById('bottom').value;
			var a = avatarList();
			if (!a)
				return;
			addAvatar({id: id, x: x, y: y, x2: x2, y2: y2, w: o.naturalWidth, h: o.naturalHeight, img: o.src}, a);
			save(a);
		});
		o.appendChild(ne);
		o.appendChild(document.createElement('br'));
		o.appendChild(document.createElement('br'));
		ne = o.appendChild(document.createElement('a'));
		newStoragePopupLink(ne);
		var ra = [(/(^\?|&)x=((\d*\.)?\d+)(&|$)/.exec(location.search)), (/(^\?|&)y=((\d*\.)?\d+)(&|$)/.exec(location.search)), (/(^\?|&)x2=((\d*\.)?\d+)(&|$)/.exec(location.search)), (/(^\?|&)y2=((\d*\.)?\d+)(&|$)/.exec(location.search))];
		if (!ra[0] || !ra[1] || !ra[2] || !ra[3])
			return;
		if (document.readyState == 'complete')
			replaceCropper(parseFloat(ra[0][2]), parseFloat(ra[1][2]), parseFloat(ra[2][2]), parseFloat(ra[3][2]));
		else
			window.addEventListener('load', function () {
				replaceCropper(parseFloat(ra[0][2]), parseFloat(ra[1][2]), parseFloat(ra[2][2]), parseFloat(ra[3][2]));
			});
	}
}