View images in local directories. Can navigate, zoom, rotate.
当前为
// ==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">⚙</a><a href="#" title="Image list">☰</a><a href="#" title="Jump to image">↳</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 (°): <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;">✖</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 = '∅';
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');
};
*/