Local Image Viewer

View images in local directories. Can navigate, zoom, rotate.

当前为 2016-05-20 提交的版本,查看 最新版本

// ==UserScript==
// @name        Local Image Viewer
// @description View images in local directories. Can navigate, zoom, rotate.
// @namespace   localimgviewer
// @include     file:///*
// @version     15
// @grant       GM_getValue
// @grant       GM_setValue
// @grant       GM_deleteValue
// @grant       GM_listValues
// ==/UserScript==

var fullAddress = window.location.href; // full address
var folderAddress = window.location.href;
var img = null;
var imgWidth = null, imgHeight = null;
var imageList = [];
var settingsObj = { 'imagedisplay': 0, 'imagezoom': 100, 'imagerotation': 0, 'imageopacity': 1, 'panelsopacity': 1, };
var imageName = '';
var curPos = null, nextPos = null, prevPos = null;

if(isAnImage(fullAddress)) {
	folderAddress = fullAddress.substring(0, fullAddress.lastIndexOf('/')) + '/';
	img = document.getElementsByTagName('img')[0];
	img.removeAttribute('width'); img.removeAttribute('height'); img.removeAttribute('class');
	if(GM_getValue('settings') !== undefined) settingsObj = JSON.parse(GM_getValue('settings'));
	handleImage();
	hookKeys();
	createPanels();
	applySettings();
}
else {
	if(fullAddress[fullAddress.length - 1] != '/') window.location.assign(fullAddress + '/');
	populateImageArray();
	document.addEventListener('click', function() { populateImageArray(); });
}

function populateImageArray() {
	imageList = [];
	let links = document.getElementsByTagName('a');
	for(let i = 0, j = ''; i < links.length; i++) {
		j = links[i].getAttribute('href');
		j = j.indexOf('/') != -1 ? j.substring(j.lastIndexOf('/') + 1) : j; // if the href contains full addr, just get the string after last slash
		if(isAnImage(j)) imageList.push(j);
	}
	if(imageList.length) { curPos = 0; nextPos = 0; prevPos = imageList.length - 1; hookKeys(); }
	GM_setValue('imagelist_' + folderAddress, JSON.stringify(imageList));
}

function handleImage() {
	img.style.position = 'absolute';
	img.style.textAlign = 'center';
	img.style.margin = 'auto';
	img.style.top = '0';
	img.style.right = '0';
	img.style.bottom = '0';
	img.style.left = '0';
	document.body.style.background = '#222';

	imageName = fullAddress.substring(fullAddress.lastIndexOf('/') + 1);
	imageList = JSON.parse(GM_getValue('imagelist_' + folderAddress));
	curPos = imageList.indexOf(imageName);
	nextPos = curPos == imageList.length - 1 ? 0 : curPos + 1;
	prevPos = curPos == 0 ? imageList.length - 1 : curPos - 1;
	imgWidth = img.naturalWidth;
	imgHeight = img.naturalHeight;
}

function isAnImage(x) {
	var ext = x.split('.').pop();
	if(ext == 'jpg' || ext == 'jpeg' || ext == 'bmp' || ext == 'png' || ext == 'gif' || ext == 'tga') return true;
	return false;
}

function hookKeys() {
	document.addEventListener('keydown', function(e) {
		let key = e.keyCode || e.which;
		let spKeys = e.ctrlKey || e.shiftKey || e.altKey;
		// console.log('keydown key: ' + key + ' spkeys: ' + spKeys);
		// if(e.ctrlKey && key == 46) { deletesettings(); console.log('deleting settings'); }
		if(spKeys) return;
		if(document.activeElement.tagName == 'INPUT') {
			if(key == 27) document.getElementById('imgViewer-prefs').lastChild.click(); // key ESC
			return;
		}
		if(key == 39) window.location.assign(imageList[nextPos]); // right arrow key
		else if(key == 37) window.location.assign(imageList[prevPos]); // left arrow key
		else if(key == 220) { if(fullAddress != folderAddress) window.location.assign(folderAddress); } // \ key
		else if(key == 188 || key == 190 || key == 191) {
			let curRotation = parseInt(document.getElementById('imgViewer-settings').getElementsByTagName('input')[4].value);
			if(key == 188) document.getElementById('imgViewer-settings').getElementsByTagName('input')[4].value = curRotation - 10; // , key
			else if(key == 190) document.getElementById('imgViewer-settings').getElementsByTagName('input')[4].value = curRotation + 10; // . key
			else if(key == 191) document.getElementById('imgViewer-settings').getElementsByTagName('input')[4].value = 0; // / key
			if(document.getElementById('imgViewer-settings').getElementsByTagName('input')[4].value < 0) document.getElementById('imgViewer-settings').getElementsByTagName('input')[4].value = 350;
			document.getElementById('imgViewer-settings').getElementsByTagName('input')[4].onchange();
		}
	});
	document.addEventListener('keypress', function(e) {
		let key = e.keyCode || e.which;
		let spKeys = e.ctrlKey || e.shiftKey || e.altKey;
		// console.log('keypress key: ' + key + ' spkeys: ' + spKeys);
		if(spKeys) return;
		if(document.activeElement.tagName == 'INPUT') return;
		if(key == 48) {
			let nextDisplay = settingsObj['imagedisplay'] == 2 ? 0 : settingsObj['imagedisplay'] + 1;
			document.getElementById('imgViewer-settings').getElementsByTagName('input')[nextDisplay].click();
		}
		else if(key == 45 || key == 61) {
			let curZoom = parseInt(document.getElementById('imgViewer-settings').getElementsByTagName('input')[3].value);
			document.getElementById('imgViewer-settings').getElementsByTagName('input')[2].click();
			if(key == 45) document.getElementById('imgViewer-settings').getElementsByTagName('input')[3].value = curZoom - 5; // - key
			else if(key == 61) document.getElementById('imgViewer-settings').getElementsByTagName('input')[3].value = curZoom + 5; // = key
			document.getElementById('imgViewer-settings').getElementsByTagName('input')[3].onchange();
		}
		else if(key == 103) document.getElementById('imgViewer-prefs').lastChild.click(); // G key
	});
}

function createPanels() {
	let mainDiv = document.createElement('div');
	mainDiv.id = 'imgViewer';
	mainDiv.style = 'color: #BBB; font-family: Tahoma, sans-serif; font-size: 13px; position: fixed; z-index: 5; top: 5px; left: 5px;';
	mainDiv.innerHTML = '<div id="imgViewer-prefs"></div><div id="imgViewer-imageinfo"></div><div id="imgViewer-settings"></div><div id="imgViewer-filelist"></div><div id="imgViewer-jump"></div>';
	document.body.appendChild(mainDiv);

	let prefsDiv = document.getElementById('imgViewer-prefs');
	let infoDiv = document.getElementById('imgViewer-imageinfo');
	let settingsDiv = document.getElementById('imgViewer-settings');
	let listDiv = document.getElementById('imgViewer-filelist');
	let jumpDiv = document.getElementById('imgViewer-jump');

	prefsDiv.innerHTML = '<a href="#" title="Image info"><b><i>i</i></b></a><a href="#" title="Settings">&#9881;</a><a href="#" title="Image list">&#9776;</a><a href="#" title="Jump to image">&#8627;</a>';
	infoDiv.innerHTML += '<span class="panel-title">Image ' + (curPos + 1) + '/' + imageList.length + '</span>';
	infoDiv.innerHTML += '<span style="color: #AF3; font-size: 16px; display: block;">' + decodeURIComponent(imageName) + '</span>';
	infoDiv.innerHTML += '<span style="font-family: sans-serif; font-size: 85%; display: block; margin-bottom: 16px;" id="zoomInfo"></span>';
	infoDiv.innerHTML += '<span style="font-size: 85%; display: block;"><a href="' + imageList[prevPos] + '">Prev</a> ' + decodeURIComponent(imageList[prevPos]) + '</span>';
	infoDiv.innerHTML += '<span style="font-size: 85%; display: block;"><a href="' + imageList[nextPos] + '">Next</a> ' + decodeURIComponent(imageList[nextPos]) + '</span>';
	settingsDiv.innerHTML += '<span class="panel-title">Settings</span>';
	settingsDiv.innerHTML += '<span>Image size: <label style="width: 100%;"><input type="radio" name="imgsize" value="0">Original</label> <label><input type="radio" name="imgsize" value="1">Fit to screen</label> <label><input type="radio" name="imgsize" value="2">Zoom (%) <input type="number" min="5" max="5000" default="100" style="width: 70px;"> <a href="#"></a></label></span>';
	settingsDiv.innerHTML += '<label>Image rotation (&deg;): <input type="number" min="0" max="360" default="0" style="width: 50px;"> <a href="#"></a></label>';
	settingsDiv.innerHTML += '<label>Image opacity: <input type="range" min="5" max="100" default="100" style="font-size: 1px; width: 90px; height: 6px;"> <span></span>% <a href="#"></a></label>';
	settingsDiv.innerHTML += '<label>Panels opacity: <input type="range" min="5" max="100" default="100" style="font-size: 1px; width: 90px; height: 6px;"> <span></span>% <a href="#"></a></label>';
	listDiv.innerHTML += '<span class="panel-title">Image list</span><select size="15"></select>';
	jumpDiv.innerHTML += '<form name="jumpToImage"><input type="number" style="width: 50px; height: 100%; font-size: 10px;"> <input type="submit" value="Jump" style="width: 50px; height: 100%; font-size: 10px;"></form>';

	let panels = mainDiv.getElementsByTagName('div');
	for(let i=0; i<panels.length; i++) {
		panels[i].style = 'background-color: #111; margin-bottom: 8px; padding: 8px 14px 14px 12px; border-radius: 6px; border: 1px solid #3A3A3A; display: table;';
		if(i == 0) { panels[i].style.fontSize = '16px'; panels[i].style.textAlign = 'center'; panels[i].style.backgroundColor = 'rgba(0,0,0,0.8)'; panels[i].style.borderRadius = '30px'; panels[i].style.padding = '0px 11px 2px'; }
		else if(i == 4) panels[i].style = 'position: absolute; top: 0; left: 116px; background-color: #18F; padding: 2px; border-radius: 6px; width: 110px; height: 20px; font-size: 10px; text-align: center;';
	}
	let links = document.getElementsByTagName('a');
	for(let i=0; i<links.length; i++) links[i].style = 'color: #FFF; text-decoration: none';
	let panelTitle = mainDiv.getElementsByClassName('panel-title');
	let prefs = prefsDiv.getElementsByTagName('a');
	for(let i=0; i<panelTitle.length; i++) {
		panelTitle[i].style = 'position: relative; color: #3AF; font-family: Georgia; font-size: 16px; margin-left: -4px; display: block; margin-bottom: 8px; padding-right: 20px;';
		panelTitle[i].innerHTML += '<a href="#" style="color: #FFF; text-decoration: none; position: absolute; top: -7; right: -8;">&#10006;</a>';
		panelTitle[i].children[0].onclick = function() {
			prefs[i].click();
			return false;
		};
	}
	let resets = settingsDiv.getElementsByTagName('a');
	for(let i=1; i<resets.length; i++) {
		resets[i].innerHTML = '&#x2205;';
		resets[i].style.color = '#F22';
		resets[i].onclick = function() {
			if(this.previousElementSibling.tagName == 'INPUT') {
				this.previousElementSibling.value = this.previousElementSibling.getAttribute('default');
				this.previousElementSibling.onchange();
			}
			else {
				this.previousElementSibling.previousElementSibling.value = this.previousElementSibling.previousElementSibling.getAttribute('default');
				this.previousElementSibling.previousElementSibling.onchange();
			}
			return false;
		};
	}
	for(let i=0; i<prefs.length; i++) {
		prefs[i].style.display = 'inline-block'; prefs[i].style.width = '21px'; prefs[i].style.height = '21px';
		prefs[i].onclick = function() {
			let j = mainDiv.children[i+1];
			if(i == 3) { j.style.display = j.style.display == 'none' ? 'block' : 'none'; j.children[0].children[0].focus(); }
			else j.style.display = j.style.display == 'none' ? 'table' : 'none';
			settingsObj['showpanels' + (i+1)] = j.style.display;
			GM_setValue('settings', JSON.stringify(settingsObj));
			return false;
		};
	}
	let fileList = listDiv.getElementsByTagName('select')[0];
	for(let i=0; i<imageList.length; i++) { fileList.innerHTML += '<option value="' + i + '" ' + (i==curPos ? 'selected' : '') + '>' + '(' + (i + 1) + ') ' + decodeURIComponent(imageList[i]) + '</option>'; }
	fileList.onchange = function() { window.location.assign(imageList[this.value]); };

	let inputs = settingsDiv.getElementsByTagName('input');
	for(let i=0; i<inputs.length; i++) {
		if(i == 0) { inputs[i].parentElement.style = 'display: inline-block;'; inputs[i].parentElement.parentElement.style = 'display: block; margin-bottom: 8px;'; }
		else if(i == 1 || i == 2) inputs[i].parentElement.style = 'display: block; margin-left: 71px';
		else if(i >= 4) inputs[i].parentElement.style = 'display: block; margin-bottom: 8px;';

		inputs[i].onchange = function() {
			if(i == 0 || i == 1 || i == 2) {
				img_doDisplay(this.value);
				if(i == 2) img_doZoom(this.nextElementSibling.value);
			}
			else if(i == 3) {
				this.previousElementSibling.click();
				this.value = this.value == '' ? settingsObj['imagezoom'] : this.value >= 5000 ? 5000 : this.value < 5 ? 5 : this.value;
				img_doZoom(this.value);
			}
			else if(i == 4) { // rotate
				this.value = this.value == '' ? 0 : this.value >= 360 ? 0 : this.value < 0 ? 0 : this.value;
				img_doRotation(this.value);
			}
			else if(i == 5 || i == 6) { // opacity
				this.value = this.value > 100 ? 100 : this.value < 5 ? 5 : this.value ? this.value : 5;
				this.nextElementSibling.innerHTML = this.value;
				if(i == 5) { img.style.opacity = this.value / 100; settingsObj['imageopacity'] = this.value / 100; }
				else { mainDiv.style.opacity = this.value / 100; settingsObj['panelsopacity'] = this.value / 100; }
				GM_setValue('settings', JSON.stringify(settingsObj));
			}
			zoomInfo();
		};
	}
	img.addEventListener('click', function() {
		if(imgWidth > window.innerWidth || imgHeight > window.innerHeight) {
			if(img.width == imgWidth && img.height == imgHeight) document.getElementById('imgViewer-settings').getElementsByTagName('input')[0].click();
			else document.getElementById('imgViewer-settings').getElementsByTagName('input')[1].click();
		}
	});
	document.forms["jumpToImage"].onsubmit = function() {
		let page = this.children[0].value;
		if(page == '') { alert('Type the image number and press Jump'); return false; }
		page = parseInt(page);
		if(page <= 0 || page > imageList.length) alert('Image #' + page + ' doesn\'t exist');
		else window.location.href = folderAddress + imageList[page - 1];
		return false;
	};
}

function img_doDisplay(fit) {
	if(fit == 0) {
		img.removeAttribute('width');
		img.removeAttribute('height');
	}
	else {
		img.removeAttribute('width');
		img.removeAttribute('height');
		if(imgWidth > window.innerWidth) {
			img.width = window.innerWidth;
			if(imgHeight > window.innerHeight) {
				img.height = window.innerHeight;
				img.removeAttribute('width');
			}
		}
		else if(imgHeight > window.innerHeight) {
			img.height = window.innerHeight;
			if(imgWidth > window.innerWidth) {
				img.width = window.innerWidth;
				img.removeAttribute('height');
			}
		}
		else { img.width = imgWidth; img.height = imgHeight; }
	}
	settingsObj['imagedisplay'] = parseInt(fit);
	GM_setValue('settings', JSON.stringify(settingsObj));
}

function img_doZoom(percent) {
	if(percent == 100) {
		img.removeAttribute('width');
		img.removeAttribute('height');
	}
	else {
		img.width = percent / 100 * imgWidth;
		img.height = percent / 100 * imgHeight;
	}
	settingsObj['imagezoom'] = parseInt(percent);
	GM_setValue('settings', JSON.stringify(settingsObj));
}

function img_doRotation(angle) {
	if(angle == 0) img.style.transform = '';
	else img.style.transform = 'rotate(' + angle + 'deg)';
	settingsObj['imagerotation'] = parseInt(angle);
	GM_setValue('settings', JSON.stringify(settingsObj));
}

function zoomInfo() {
	let text = '';
	if(settingsObj['imagedisplay'] == 0) text = 'Original';
	else if(settingsObj['imagedisplay'] == 1) text = 'Fit to screen';
	else if(settingsObj['imagedisplay'] == 2) text = settingsObj['imagezoom'] + '% zoom';
	document.getElementById('zoomInfo').innerHTML = text + ' (' + img.width + 'x' + img.height + ')';
	GM_setValue('settings', JSON.stringify(settingsObj));
}

function applySettings() {
	console.log(settingsObj);
	if(settingsObj['imagezoom'] !== undefined) document.getElementById('imgViewer-settings').children[1].children[2].children[1].value = settingsObj['imagezoom'];
	if(settingsObj['imagedisplay'] !== undefined) {
		document.getElementById('imgViewer-settings').children[1].children[settingsObj['imagedisplay']].children[0].click();
		if(document.getElementById('imgViewer-settings').children[1].children[2].children[0].checked == true) img_doZoom(settingsObj['imagezoom']);
	}
	if(settingsObj['imagerotation'] !== undefined) {
		document.getElementById('imgViewer-settings').children[2].children[0].value = settingsObj['imagerotation'];
		img_doRotation(settingsObj['imagerotation']);
	}
	if(settingsObj['imageopacity'] !== undefined) {
		img.style.opacity = settingsObj['imageopacity'];
		document.getElementById('imgViewer-settings').children[3].children[0].value = settingsObj['imageopacity'] * 100;
		document.getElementById('imgViewer-settings').children[3].children[1].innerHTML = settingsObj['imageopacity'] * 100;
	}
	if(settingsObj['panelsopacity'] !== undefined) {
		document.getElementById('imgViewer').style.opacity = settingsObj['panelsopacity'];
		document.getElementById('imgViewer-settings').children[4].children[0].value = settingsObj['panelsopacity'] * 100;
		document.getElementById('imgViewer-settings').children[4].children[1].innerHTML = settingsObj['panelsopacity'] * 100;
	}
	let j = document.getElementById('imgViewer');
	for(let i=1; i<j.children.length; i++) { if(settingsObj['showpanels' + i] !== undefined) j.children[i].style.display = settingsObj['showpanels' + i]; }
	zoomInfo();
}

/*
function deletesettings() {
	var keys = GM_listValues();
	for (var i=0, key=null; key=keys[i]; i++) GM_deleteValue(key);
	alert('all settings deleted');
};
*/

QingJ © 2025

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