Something Awful Image Fixes

Smarter image handling on the Something Awful forums.

当前为 2015-05-17 提交的版本,查看 最新版本

// ==UserScript==
// @name			Something Awful Image Fixes
// @namespace		SA
// @description		Smarter image handling on the Something Awful forums.
// @include			http://forums.somethingawful.com/*
// @version			1.2.0
// @grant			GM_openInTab
// @run-at			document-end
// @icon			http://forums.somethingawful.com/favicon.ico
// ==/UserScript==

var Util = {
	assetsLoaded: false,
	assetsLoading: 0,

	/**
	 * Initialise the page, strip out any assets that we will load.
	 */
	initialise: function(target) {
		// Remove content images:
		var images = document.querySelectorAll('td.postbody img');

		for (var index in images) {
			var image = images[index];

			if (typeof image !== 'object') continue;

			var src = image.getAttribute('src');

			// Exclude smilies:
			if (!/somethingawful[.]com[/](images[/]smilies|forumsystem[/]emoticons)[/]/.test(src)) {
				var placeholder = document.createElement('span');

				placeholder.setAttribute('data-saif-pending', 'yes');
				placeholder.saifCreate = src;

				image.parentNode.replaceChild(placeholder, image);
			}
		}

		// Remove other images:
		var images = document.querySelectorAll('img');

		for (var index in images) {
			var image = images[index];

			if (!image.parentNode) continue;

			var placeholder = document.createElement('span');

			placeholder.setAttribute('data-saif-pending', 'yes');
			placeholder.saifClone = image;

			image.parentNode.replaceChild(placeholder, image);
		}

		// Remove embedded videos:
		var iframes = document.querySelectorAll('td.postbody iframe');

		for (var index in iframes) {
			var iframe = iframes[index];

			if (!iframe.parentNode) continue;

			var placeholder = document.createElement('span');

			placeholder.setAttribute('data-saif-pending', 'yes');
			placeholder.saifClone = iframe;

			iframe.parentNode.replaceChild(placeholder, iframe);
		}

		// Fix post table styles:
		var posts = document.querySelectorAll('table.post');

		for (var index in posts) {
			var post = posts[index];

			if (typeof post !== 'object') continue;

			post.style.tableLayout = 'fixed';
		}

		Util.beginLoading(target);
	},

	/**
	 * Begin loading assets from the start of the document
	 * until and including the windows viewport.
	 */
	beginLoading: function(target) {
		var offset = window.scrollY + window.innerHeight;

		if (!!target) {
			offset = Util.getElementOffset(target) + window.innerHeight
		}

		// Initialise all elements up until the offset:
		var placeholders = document.querySelectorAll('[data-saif-pending]'),
			queue = [];

		for (var index in placeholders) {
			var placeholder = placeholders[index];

			if (typeof placeholder !== 'object') continue;

			if (Util.getElementOffset(placeholder) < offset) {
				queue.push(placeholder);
			}
		}

		for (var index in queue) {
			Util.createElement(queue[index]);
		}

		// Wait until everything initialised thus far is loaded:
		if (!!target) {
			Util.waitForReady(function() {
				// Scroll to the target element:
				window.scrollTo(0, Util.getElementOffset(target));

				// Resume loading of images and videos:
				Util.resumeLoading();
			});
		}

		// No reason to wait:
		else {
			Util.resumeLoading();
		}
	},

	/**
	 * Resume loading assets not handled by `Util.beginLoading`
	 * as they become visible in the windows viewport.
	 */
	resumeLoading: function() {
		var placeholders = document.querySelectorAll('[data-saif-pending]');

		for (var index in placeholders) {
			var placeholder = placeholders[index];

			Util.waitForVisibility(placeholder, function(placeholder) {
				Util.createElement(placeholder);
			});
		}
	},

	/**
	 * Create an asset element from a placeholder.
	 */
	createElement: function(placeholder) {
		if (!placeholder.parentNode) return;

		// No processing needs to be done:
		if (!!placeholder.saifClone) {
			var element = placeholder.saifClone.cloneNode(true);

			// Track the loading of this image:
			if (element instanceof HTMLImageElement) {
				Util.trackLoadState(element, ['load']);
			}

			placeholder.parentNode.replaceChild(element, placeholder);
		}

		// Process the source of this image:
		else if (!!placeholder.saifCreate) {
			var src = placeholder.saifCreate;

			if (/i\.imgur\.com/.test(src)) {
				Util.createImgur(placeholder, src);
			}

			else if (/staticflickr\.com\//.test(src)) {
				Util.createFlickr(placeholder, src);
			}

			else {
				Util.createImage(placeholder, src);
			}
		}
	},

	/**
	 * Create an empty element indicating of failure.
	 */
	createEmpty: function(placeholder) {
		var span = document.createElement('span');

		span.setAttribute('data-saif-empty', 'yes');

		placeholder.parentNode.replaceChild(span, placeholder);
	},

	/**
	 * Create a simple image element from a given source URL.
	 */
	createImage: function(placeholder, src, href) {
		var image = document.createElement('img');

		image.setAttribute('src', src);

		// Make sure the image is styled correctly:
		Util.setElementStyles(image);

		// Track the loading of this image:
		Util.trackLoadState(image, ['load']);

		// Append the image to the page when it is loaded:
		image.addEventListener('load', function() {
			if (!!href) {
				var link = document.createElement('a');

				link.setAttribute('href', href);
				link.appendChild(image);

				placeholder.parentNode.replaceChild(link, placeholder);
			}

			else {
				placeholder.parentNode.replaceChild(image, placeholder);
			}
		});
	},

	/**
	 * Create a video element from a list of source URLs with media types.
	 */
	createVideo: function(placeholder, src, href, sources) {
		var video = document.createElement('video');

		// Set attributes to ensure gif style playback:
		video.setAttribute('preload', 'auto');
		video.setAttribute('autoplay', 'autoplay');
		video.setAttribute('muted', 'muted');
		video.setAttribute('loop', 'loop');
		video.setAttribute('webkit-playsinline', 'webkit-playsinline');

		// Make sure the video is styled correctly:
		Util.setElementStyles(video);

		// Video has loaded, insert it or a fallback:
		video.addEventListener('loadeddata', function() {
			if (
				(video.videoWidth < 125 && video.videoHeight < 200)
				|| (video.videoHeight < 125 && video.videoWidth < 200)
			) {
				video.pause();
				Util.createImage(placeholder, src, href);
			}

			else {
				placeholder.parentNode.replaceChild(video, placeholder);
			}
		});

		// Track the loading of this video:
		Util.trackLoadState(video, ['loadeddata', 'error']);

		// Add media sources:
		for (var index in sources) {
			var source = document.createElement('source');

			source.setAttribute('src', sources[index][0]);
			source.setAttribute('type', sources[index][1]);

			video.appendChild(source);
		}
	},

	/**
	 * Create an imgur image or video from a given source URL.
	 */
	createImgur: function(placeholder, src) {
		var bits = /\/(.{5}|.{7})[hls]?\.(jpg|png|gif)/i.exec(src);

		// Could not parse the image:
		if (bits) {
			var identity = bits[1],
				extension = bits[2].toLowerCase();

			// Is a video:
			if ('gif' === extension) {
				Util.createVideo(
					placeholder,
					'//i.imgur.com/' + identity + '.' + extension,
					'//i.imgur.com/' + identity + '.' + extension,
					[
						['//i.imgur.com/' + identity + '.webm',	'video/webm'],
						['//i.imgur.com/' + identity + '.mp4',	'video/mp4']
					]
				);
			}

			// Is an image:
			else {
				Util.createImage(
					placeholder,
					'//i.imgur.com/' + identity + 'h.' + extension,
					'//i.imgur.com/' + identity + '.' + extension
				);
			}
		}

		// The source was invalid:
		else {
			Util.createEmpty(placeholder);
		}
	},

	/**
	 * Create a flickr image from a given source URL.
	 */
	createFlickr: function(placeholder, src) {
		var bits = /^(.+?\.com\/.+?\/.+?_.+?)(_[omstzb])?\.(.+?)$/.exec(src),
			location,
			extension;

		// Create an image:
		if (bits) {
			var location = bits[1],
				extension = bits[3].toLowerCase();

			Util.createImage(
				placeholder,
				location + '_b.' + extension,
				location + '_b.' + extension
			);
		}

		// The source was invalid:
		else {
			Util.createEmpty(placeholder);
		}
	},

	/**
	 * Calculate the offset from the top of the page to the
	 * top of the given element.
	 */
	getElementOffset: function(element) {
		var offset = 0;

		while (element.offsetParent) {
			offset += element.offsetTop;
			element = element.offsetParent;
		}

		return offset;
	},

	/**
	 * Style an element so that it cannot break out of the post table.
	 */
	setElementStyles: function(element) {
		element.style.display = 'inline-block';
		element.style.marginBottom = '5px';
		element.style.marginTop = '5px';
		element.style.maxWidth = '100%';
	},

	/**
	 * Attach events to count the number of currently loading assets.
	 */
	trackLoadState: function(element, eventNames) {
		if (Util.assetsLoaded) return;

		Util.assetsLoading++;

		for (var index in eventNames) {
			element.addEventListener(eventNames[index], Util.trackReadyState);
		}
	},

	/**
	 * The attached event handler for `Util.trackLoadState`.
	 */
	trackReadyState: function(event) {
		if (Util.assetsLoaded) return;

		Util.assetsLoading--;

		if (0 === Util.assetsLoading) {
			Util.assetsLoaded = true;
		}
	},

	/**
	 * Wait for all of the assets loaded in `Util.beginLoading`
	 * to complete.
	 */
	waitForReady: function(callback) {
		var wait = setInterval(function() {
			if (Util.assetsLoaded) {
				clearInterval(wait);
				callback();
			}
		}, 1);
	},

	/**
	 * Wait for the user to scroll within two pages of an element
	 * and then call the callback.
	 */
	waitForVisibility: function(element, callback) {
		var chromeSucks = false;
		var scroll = function() {
			if (chromeSucks) return;

			var offset = Util.getElementOffset(element),
				max = window.scrollY + (window.innerHeight * 2);

			if (max > offset) {
				chromeSucks = true;
				window.removeEventListener('scroll', scroll);
				callback(element);
			}
		};

		scroll();
		window.addEventListener('scroll', scroll);
	}
};

try {
	var forEach = Array.prototype.forEach,
		offset = window.outerHeight;

	// Prevent images from loading:
	window.stop();

	// Redirect the page:
	if (document.querySelectorAll('meta[http-equiv=refresh]').length) {
		var rule = document.querySelectorAll('meta[http-equiv=refresh]')[0].getAttribute('content');

		if (/URL=(.+)$/.test(rule)) {
			window.location = /URL=(.+)$/.exec(rule)[1];
		}
	}

	// Jump to appropriate place on page:
	else if (!!window.location.hash && document.querySelectorAll(window.location.hash).length) {
		Util.initialise(document.querySelectorAll(window.location.hash)[0]);
	}

	// Load the page normally:
	else {
		Util.initialise();
	}
}

catch (e) {
	console.log("Exception: " + e.name + " Message: " + e.message);
}

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址