/*
IMGXIS made by YellowAfterlife. http://yellowafterlife.itch.io/imgxis (mirror: https://github.com/Cynosphere/IMGXIS)
Ported by Jane (maybejane/statefram) and Cynthia (BoxOfFlex/Cynosphere) to a private client mod.
Re-ported by Cynthia to EndPwn.
*/
exports.manifest = {
author: "Cynosphere, YellowAfterlife",
name: "IMGXIS",
description: "A better way to view images."
}
exports.start = function(){
var globalPanner;
var values = {};
/*function _hook() {
const imageWrapper = document.querySelectorAll(
'div[class^="modal-"] div[class^="inner-"] div div[class^="imageWrapper-"]'
)[0];
const image = imageWrapper.children[0].className == "imgxis-panner" ? imageWrapper.children[0].children[0] : imageWrapper.children[0];
if (imageWrapper && image) {
onImageOpen(image);
} else {
onImageClose(globalPanner);
}
}
window._hook = _hook;*/
function onLoad() {
var css = document.createElement("style");
css.type = "text/css";
css.id = "css-imgxis";
var menuButtonWidth = 32; // in pixels
css.innerHTML = [
"body {",
" background-image: none;",
" overflow: hidden;",
"}",
// panner area
".imgxis-panner {",
" position: absolute;",
" left: 0; width: 100%;",
" top: 0; height: 100%;",
"}",
// cursors
".imgxis-panner, .imgxis-panner img {",
" cursor: move;",
"}",
".imgxis-panner.colorpick, .imgxis-panner.colorpick img {",
" cursor: default;",
" cursor: copy;",
"}",
// disable interpolation:
".imgxis-panner.zoomed, .imgxis-panner.zoomed img {",
" -ms-interpolation-mode: nearest-neighbor;",
" image-rendering: optimizeSpeed;",
" image-rendering: -moz-crisp-edges;",
" image-rendering: -webkit-optimize-contrast;",
" image-rendering: -o-crisp-edges;",
" image-rendering: pixelated;",
"}",
// actual image:
".imgxis-panner img {",
// disable Firefox' centering:
" position: relative;",
" margin: 0;",
"}",
// menu pane on top:
".imgxis-menu {",
" position: absolute;",
" left: 0; width: 100%;",
" top: 0; height: 24px;",
" background: rgba(0, 0, 0, 0.7);",
" padding: 4px;",
" color: white;",
" font: 16px sans-serif;",
" line-height: 24px;",
" min-width: 1024px;",
"}",
// style the buttons in the menu:
".imgxis-menu input {",
` width: ${menuButtonWidth}px;`,
" height: 24px;",
" padding: 0px;",
" margin-right: 2px;",
" float: left;",
"}"
].join("\n");
document.head.appendChild(css);
}
onLoad();
function onImageOpen(element) {
let wrap = document.querySelector('div[class*="imageWrapper-"]');
const openOrig = document.querySelectorAll('div a[class*="downloadLink-"]')[0];
openOrig.style.display = "none";
const origLink = openOrig.getAttribute("href");
var menuColors = 10; // how many color buttons to show in the menu
// ensure that the page contains nothing but a single :
var q = element;
if (q.tagName != "IMG") return;
// stylesheet:
// the thing is that browser has it's own zoom in controls, and they are
// linked to that particular natively. So we need to make a copy of it, strip
// it of any unneeded styles, and put it in place of the original:
var o = q.cloneNode();
//remove size downscaling
let reg = /\?width=\d+&height=\d+$/;
let os = o.getAttribute("src");
let repl = os.replace(reg, "");
o.setAttribute("src", repl);
//
o.removeAttribute("width");
o.removeAttribute("height");
var panner = document.createElement("div");
globalPanner = panner;
function check() {
if (!wrap) {
onImageClose(globalPanner);
}
}
var pannerZoomed = false;
panner.className = "imgxis-panner";
panner.style.height = "100vh";
panner.style.width = "100vw";
panner.appendChild(o);
q.parentNode.appendChild(panner);
//q.parentNode.removeChild(q);
q.style.display = "none";
function fixWrap() {
let elems = document.querySelectorAll(
'div[class^="modal-"] > div[class^="inner-"] > div > div[class^="imageWrapper-"]'
);
elems.forEach(el => {
fixElem(el);
});
}
function fixElem(elemToFix) {
if (electron.process.platform == "win32" || electron.process.platform == "darwin") {
menu.style.zIndex = 3003;
menu.style.top = $('[class*="typeWindows-"]').clientHeight+"px";
menu.style.position = "absolute";
menu.style.left = 0;
elemToFix.style.top = $('[class*="typeWindows-"]').clientHeight+"px";
elemToFix.style.height = `calc(100vh - ${$('[class*="typeWindows-"]').clientHeight}px)`;
} else {
elemToFix.style.height = "100vh";
elemToFix.style.top = 0;
}
elemToFix.style.width = "100vw";
elemToFix.style.position = "absolute";
elemToFix.style.zIndex = 3002;
elemToFix.style.left = 0;
}
var s = o.style;
s.transformOrigin = "top left";
q = null;
var tile = false;
//
var w = 0,
h = 0,
x = 0,
y = 0,
z = 0,
m = 1;
// pan and zoom:
function update() {
var pz = m > 1;
if (pz != pannerZoomed) {
pannerZoomed = pz;
var cl = panner.classList;
if (pz) cl.add("zoomed");
else cl.remove("zoomed");
}
if (tile) {
var p = panner.style;
p.backgroundPosition = `${-x}px ${-y}px`;
p.backgroundSize = `${o.width * m}px`;
}
s.transform = `matrix(${m},0,0,${m},${-x},${-y})`;
}
function zoomto(zx, zy, d) {
var p = m;
m = Math.pow(2, (z += d));
var f = m / p;
x = (zx + x) * f - zx;
y = (zy + y) * f - zy;
menu_zoom.innerHTML = (0 | (m * 100)) + "%";
update();
}
// menu:
var menu_zoom = null,
menu;
(function() {
menu = document.createElement("div");
menu.style.zIndex = 2000;
menu.className = "imgxis-menu";
function menubt(s, f, style) {
var bt = document.createElement("input");
bt.type = "button";
bt.value = s;
if (style) {
bt.style = style;
}
bt.addEventListener("click", f);
menu.appendChild(bt);
}
var defcolors = [
"#6A86B7",
"#60C19D",
"#AB7680",
"#FFFFFF",
"#F4F2EC",
"#CAC2BD",
"#88898E",
"#4F556A",
"#1D1F2C",
"#000000"
];
function menucl(i) {
var bt = document.createElement("input");
bt.type = "color";
bt.style.zIndex = 1001;
bt.value = GM_getValue(`imgxis-color${i}`, defcolors[i]);
function bt_apply() {
let bodyElem = document.querySelector('div[class*="backdrop-"]');
bodyElem.style.backgroundColor = bt.value;
}
bt.addEventListener("click", function(e) {
if (!e.shiftKey) {
e.preventDefault();
bt_apply();
}
});
bt.addEventListener("change", function(e) {
GM_setValue(`imgxis-color${i}`, bt.value);
bt_apply();
});
bt.title = `Color ${i + 1}.\nClick to apply.\nShift+click to change.`;
menu.appendChild(bt);
if (i == 0) bt_apply();
}
for (var i = 0; i < menuColors; i++) menucl(i);
menubt("-", function(_) {
zoomto(window.innerWidth / 2, window.innerHeight / 2, -0.5);
});
menubt("1:1", function(_) {
var iw = window.innerWidth,
ih = window.innerHeight;
zoomto(iw / 2, ih / 2, -z);
x = (w - iw) / 2;
y = (h - ih) / 2;
update();
});
menubt("+", function(_) {
zoomto(window.innerWidth / 2, window.innerHeight / 2, 0.5);
});
menubt("tile", function(_) {
tile = !tile;
panner.style.backgroundImage = tile ? `url(${o.src})` : "";
update();
});
menubt(
"original",
function(_) {
window.open(origLink);
},
"width:50px;"
);
//
menu_zoom = document.createElement("span");
menu_zoom.innerHTML = "100%";
menu.appendChild(menu_zoom);
document
.querySelector('div[class*="backdrop-"]')
.parentNode.appendChild(menu);
})();
// mouse controls:
function onmousewheel(e) {
var d = e.wheelDelta || -e.detail;
d = (d < 0 ? -1 : d > 0 ? 1 : 0) * 0.5;
zoomto(e.pageX, e.pageY, d);
check();
}
var mx = 0,
my = 0,
mp = false;
function onmousemove(e) {
var ox = mx;
mx = e.pageX;
var dx = mx - ox;
var oy = my;
my = e.pageY;
var dy = my - oy;
if (mp) {
x -= mx - ox;
y -= my - oy;
update();
}
check();
}
function onmousedown(e) {
onmousemove(e);
fixWrap();
if (e.which != 3 && !e.altKey) {
// not the right click
e.preventDefault(); // disable image "grab"
mp = true;
}
}
function onmouseup(e) {
onmousemove(e);
fixWrap();
mp = false;
}
//
var refresh_timer = null;
function refresh() {
var o_src = o.src;
// strip the previous timestamp parameter:
var pos = o_src.indexOf("imgxis-time");
if (pos >= 0) {
switch (o_src.charAt(pos - 1)) {
case "&":
case "?":
pos--;
break;
}
o_src = o_src.substring(0, pos);
}
// add a timestamp parameter:
o_src += o_src.indexOf("?") >= 0 ? "&" : "?";
o_src += `imgxis-time=${Date.now()}`;
//
o.src = o_src;
if (tile) panner.style.backgroundImage = `url(${o.src})`;
}
//
function onkeydown(e) {
switch (e.keyCode) {
case 27:
onImageClose(globalPanner);
break;
case 116:
e.preventDefault();
if (e.shiftKey) {
if (refresh_timer == null) {
var t = parseFloat(prompt("Refresh interval (seconds)", "15"));
if (!isNaN(t))
refresh_timer = setInterval(refresh, Math.max(100, t * 1000));
} else clearInterval(refresh_timer);
} else refresh();
break;
}
}
window.addEventListener("mousemove", onmousemove);
panner.addEventListener("mousedown", onmousedown);
window.addEventListener("mouseup", onmouseup);
window.addEventListener("mousewheel", onmousewheel);
window.addEventListener("DOMMouseScroll", onmousewheel);
window.addEventListener("keydown", onkeydown);
// consider that the image dimensions may not be available instantly:
var onwait_t = 1000;
var onwait = function onwait() {
w = o.width;
h = o.height;
if (w > 0 && h > 0) {
var iw = window.innerWidth,
lw = w;
var ih = window.innerHeight,
lh = h;
if (lw < iw && lh < ih)
for (var k = 0; k < 3; k++) {
if (lw * 2 < iw && lh * 2 < ih) {
z++;
lw *= 2;
lh *= 2;
}
}
else
while (lw > iw || lh > ih) {
z--;
lw /= 2;
lh /= 2;
}
m = Math.pow(2, z);
menu_zoom.innerHTML = (0 | (m * 100)) + "%";
x = -(iw - lw) / 2;
y = -(ih - lh) / 2;
update();
fixWrap();
} else if (--onwait_t > 0) setTimeout(onwait, 10);
};
onwait();
}
function onImageClose(panner) {
if (panner) {
document.querySelectorAll('div[class*="imgxis-menu"]').forEach(el=>el.remove());
window.removeEventListener("mousemove", onmousemove);
panner.removeEventListener("mousedown", onmousedown);
window.removeEventListener("mouseup", onmouseup);
window.removeEventListener("mousewheel", onmousewheel);
window.removeEventListener("DOMMouseScroll", onmousewheel);
window.removeEventListener("keydown", onkeydown);
globalPanner = undefined;
}
}
function GM_getValue(name, defaultV) {
if (values[name]) {
return values[name];
} else {
return defaultV;
}
}
function GM_setValue(name, value) {
values[name] = value;
}
$api.events.hook("MODAL_PUSH",e=>{
setTimeout(_=>{
const imageWrapper = document.querySelectorAll('div[class^="modal-"] div[class^="inner-"] div div[class^="imageWrapper-"]')[0];
const image = (imageWrapper && imageWrapper.children[0].className == "imgxis-panner") ? imageWrapper.children[0].children[0] : (imageWrapper ? imageWrapper.children[0] : imageWrapper);
if (imageWrapper && image) {
onImageOpen(image);
}
},300);
});
$api.events.hook("MODAL_POP",e=>{
onImageClose(globalPanner);
});
}