FA - Gallery Usability Handler

Provides actual download link, shows image checkerboard and adds keyboard shortcuts

This script is marked as "external"
This script was commisioned by someone else, and I will not check if it works. If it breaks, contact me
Click here to install Browse More Scripts
// ==UserScript==
// @name         FA - Gallery Usability Handler
// @namespace    23e6f0ae9d41ec47fdf7e8670c7e1d379a23613c
// @version      4.0
// @description  Provides actual download link, shows image checkerboard and adds keyboard shortcuts
// @author       /u/AyrA_ch
// @include      https://www.furaffinity.net/view/*/
// @include      http://www.furaffinity.net/view/*/
// @external     true
// @expired      false
// @grant        GM_download
// @run-at       document-end
// ==/UserScript==

//Changelog
//=========
//4.0 - Update for new page design. Not sure if old design still works
//3.3 - Limit maximum width to container width
//3.2 - No longer triggers keyboard shortcuts on certain elements (flash for example)
//3.1 - Keyboard Shortcuts (D=Download, F=Fullview, Right=Previous, Left=Next)
//3.0 - Fix download button
//2.2 - Reformatting and description edit
//2.1 - Background checkerboard for large images
//2.0 - Added better full view handler (shows load progress)
//1.1 - Adapted for "full_url" variable no longer being present
//1.0 - Initial release

(function ($, $$) {
	'use strict';
	//Regex that matches the id from an URL
	var idMatch = /view\/(\d+)\/?$/;
	var IMG_BG = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgA" +
		"AABAAAAAQCAIAAACQkWg2AAAAJ0lEQVR42mOcOXMmAzZw9uxZrOKM" +
		"oxpooiEtLQ2rhLGx8agG+mkAACpiL/lWCxuBAAAAAElFTkSuQmCC";
	var getDownloadLink = function () {
		var all = $$(".actions a");
		for (var i = 0; i < all.length; i++) {
			if (all[i].textContent === "Download") {
				return all[i];
			}
		}
		return null;
	};

	var downloadFile = function (e) {
		var downloadLink = e.target;
		if (downloadLink && downloadLink.href) {
			e.preventDefault();
			e.stopPropagation();
			var url = downloadLink.href;
			downloadLink.textContent = "Loading...";
			GM_download({
				url: url,
				name: url.match(/[^/]+$/)[0],
				onerror: function (e) {
					console.error(e);
					alert("Unable to download file. Reason: " + e.error);
				},
				onprogress: function (e) {
					console.log("Progress:", e, arguments);
				},
				onload: function (e) {
					//Argument is always empty
					downloadLink.textContent = "Complete";
				}
			});
		}
	};

	var downloadLink = getDownloadLink();
	if (downloadLink) {
		//Make this look like a button
		downloadLink.setAttribute("class", "button-link");
	} else {
		downloadLink = $(".download>a");
	}
	//Attributes that are in use for the new and old design
	if (downloadLink) {
		//Make this a download link
		downloadLink.setAttribute("download", "");
		downloadLink.setAttribute("title", "Press [D] for quick download with full resolution");
		//Attach custom handler
		downloadLink.addEventListener("click", downloadFile);
	}

	var makeFull = function () {
		var i = $("img#submissionImg");
		if (i) {
			var rules = [
				"background-image:url(" + IMG_BG + ")",
				"min-width:" + i.clientWidth + "px",
				"min-height:" + i.clientHeight + "px",
				"max-width:100%"
			];
			var full = i.getAttribute("data-fullview-src");
			var small = i.getAttribute("data-preview-src");
			var n = document.createElement("img");
			n.setAttribute("style", rules.join(';'));
			n.setAttribute("src", full);
			n.setAttribute("data-fullview", 1);
			n.setAttribute("data-fullview-src", full);
			n.setAttribute("data-preview-src", small);
			n.title = "Click or press [F] to switch to preview";
			n.onclick = makeSmall;
			i.parentNode.replaceChild(n, i);
			n.id = "submissionImg";
		}
	};

	var makeSmall = function () {
		var i = $("img#submissionImg");
		if (i) {
			var full = i.getAttribute("data-fullview-src");
			var small = i.getAttribute("data-preview-src");
			var n = document.createElement("img");
			n.setAttribute("src", small);
			n.setAttribute("data-fullview", 0);
			n.setAttribute("data-fullview-src", full);
			n.setAttribute("data-preview-src", small);
			n.title = "Click or press [F] to switch to full view";
			n.onclick = makeFull;
			i.parentNode.replaceChild(n, i);
			n.id = "submissionImg";
		}
	};
	makeSmall();

	var swapFS = function () {
		if ($("img[data-fullview='1']")) {
			makeSmall();
		} else {
			makeFull();
		}
	};

	var currentId = function () {
		return +location.pathname.match(idMatch)[1];
	};

	var getPreviews = function () {
		//Get up to 6 other entries
		var gallery = Array.from($$(".preview-gallery-container a")).map(function (v) {
				return +v.href.match(idMatch)[1];
			});
		if (gallery.length === 0) {
			return [];
		}
		gallery.push(currentId());
		//Sort in descending order
		return gallery.sort(function (a, b) {
			return b - a;
		});
	};

	var newerImage = function (e) {
		if (e && typeof(e.click) === typeof(function () {})) {
			e.click();
		} else {
			var id = currentId();
			var p = getPreviews();
			var index = p.indexOf(id);
			if (p.length > 0 && index >= 0) {
				if (index > 0) {
					location.href = "/view/" + p[index - 1] + "/";
				} else {
					console.log("End of gallery");
				}
			} else {
				console.log("No gallery");
			}
		}
	};

	var olderImage = function (e) {
		if (e && typeof(e.click) === typeof(function () {})) {
			e.click();
		} else {
			var id = currentId();
			var p = getPreviews();
			var index = p.indexOf(id);
			if (p.length > 0 && index >= 0) {
				if (index < p.length - 1) {
					location.href = "/view/" + p[index + 1] + "/";
				} else {
					console.log("End of gallery");
				}
			} else {
				console.log("No gallery");
			}
		}
	};

	var handleNavButtons = function () {
		var oldBtn = {
			next: $("a.next"),
			prev: $("a.prev")
		};
		if (oldBtn.next) {
			oldBtn.next.title = "Press [Left Arrow] for quick access";
		}
		if (oldBtn.prev) {
			oldBtn.prev.title = "Press [Right Arrow] for quick access";
		}
		if (!getDownloadLink()) {
			var section = document.createElement("section");
			var newer = $(".buttons > .fav").cloneNode(true);
			var older = $(".buttons > .note").cloneNode(true);
			//fix copied fav link
			var link = newer.querySelector("a");
			link.href = "#";
			link.textContent = "<< Newer";
			link.title = "Press [Left Arrow] for quick access";
			link.addEventListener("click", newerImage);

			//fix copied note link
			link = older.querySelector("a");
			link.href = "#";
			link.textContent = "Older >>";
			link.title = "Press [Right Arrow] for quick access";
			link.addEventListener("click", olderImage);
			section.appendChild(newer);
			section.appendChild(older);

			section.setAttribute("class", "buttons");

			$(".submission-sidebar").insertBefore(section, $(".submission-sidebar .buttons"));
		}
	};

	document.addEventListener("keydown", function (e) {
		//We don't accept input on elements that might need them
		var skip = [
			"INPUT", "TEXTAREA", "SELECT",
			"OBJECT", "EMBED"
		];
		var c = {
			click: function () {}
		};
		if (skip.indexOf(e.target.nodeName) < 0) {
			//Don't accept arrow keys if image doesn't exists (allows arrow keys in flash)
			var img = $("img#submissionImg");
			switch (e.keyCode) {
			case 37: //Left Arrow
				if (img) {
					newerImage($("a.next"));
				}
				break;
			case 39: //Right Arrow
				if (img) {
					olderImage($("a.prev"));
				}
				break;
			case 68: //D
				if (downloadLink) {
					downloadLink.click();
				}
				break;
			case 70: //F
				swapFS();
				break;
			}
		} else {
			console.debug("Ignoring key because in focusable element");
		}

	});
	var title = $$(".cat")[0];
	if (title) {
		title.scrollIntoView();
	}

	handleNavButtons();
})(document.querySelector.bind(document), document.querySelectorAll.bind(document));

/*
LICENSE:
This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
The full license text can be found here: http://creativecommons.org/licenses/by-nc-sa/4.0/
The link has an easy to understand version of the license and the full license text.

DISCLAIMER:
THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
THE POSSIBILITY OF SUCH DAMAGE.
*/

Copyright © 2018 by Kevin Gut 📧 | More services | Generated for 3.232.129.123