/*
* This file contains image viewer for note editor.
*/
import Knowledge4Zotero from "../addon";
import AddonBase from "../module";
import { CopyHelper, pick } from "../utils";
class EditorImageViewer extends AddonBase {
_window: Window;
scaling: number;
srcList: string[];
idx: number;
title: string;
pined: boolean;
anchorPosition: {
left: number;
top: number;
};
icons: any;
constructor(parent: Knowledge4Zotero) {
super(parent);
this.scaling = 1;
this.title = "Note";
this.pined = false;
this.icons = {
pin: ``,
pined: ``,
};
}
async onInit(srcs: string[], idx: number, title: string, pined: boolean) {
if (!this._window || this._window.closed) {
this._window = window.open(
"chrome://Knowledge4Zotero/content/imageViewer.html",
"betternotes-note-imagepreview",
`chrome,centerscreen,resizable,status,width=500,height=550${
pined ? ",alwaysRaised=yes" : ""
}`
);
let t = 0;
// Wait for window
while (t < 500 && this._window.document.readyState !== "complete") {
await Zotero.Promise.delay(10);
t += 1;
}
const container = this._window.document.querySelector(
".container"
) as HTMLDivElement;
const img = this._window.document.querySelector(
"#image"
) as HTMLImageElement;
this._window.document
.querySelector("#left")
.addEventListener("click", (e) => {
this.setIndex("left");
});
this._window.document
.querySelector("#bigger")
.addEventListener("click", (e) => {
this.anchorPosition = {
left: img.scrollWidth / 2 - container.scrollLeft / 2,
top: img.scrollHeight / 2 - container.scrollLeft / 2,
};
this.setScale(this.scaling * 1.1);
});
this._window.document
.querySelector("#smaller")
.addEventListener("click", (e) => {
this.anchorPosition = {
left: img.scrollWidth / 2 - container.scrollLeft / 2,
top: img.scrollHeight / 2 - container.scrollLeft / 2,
};
this.setScale(this.scaling / 1.1);
});
this._window.document
.querySelector("#resetwidth")
.addEventListener("click", (e) => {
this.setScale(1);
});
this._window.document
.querySelector("#right")
.addEventListener("click", (e) => {
this.setIndex("right");
});
this._window.document
.querySelector("#copy")
.addEventListener("click", (e) => {
new CopyHelper().addImage(this.srcList[this.idx]).copy();
this._Addon.ZoteroViews.showProgressWindow(
"Better Notes",
"Image Copied."
);
});
this._window.document
.querySelector("#save")
.addEventListener("click", async (e) => {
let parts = this.srcList[this.idx].split(",");
if (!parts[0].includes("base64")) {
return;
}
let mime = parts[0].match(/:(.*?);/)[1];
let bstr = atob(parts[1]);
let n = bstr.length;
let u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
let ext = Zotero.MIME.getPrimaryExtension(mime, "");
const filename = await pick(
Zotero.getString("noteEditor.saveImageAs"),
"save",
[[`Image(*.${ext})`, `*.${ext}`]],
`${Zotero.getString("fileTypes.image").toLowerCase()}.${ext}`
);
if (filename) {
await OS.File.writeAtomic(
this._Addon.NoteUtils.formatPath(filename),
u8arr
);
}
this._Addon.ZoteroViews.showProgressWindow(
"Better Notes",
`Image Saved to ${filename}`
);
});
this._window.document.querySelector("#pin").innerHTML =
this.icons[pined ? "pined" : "pin"];
this._window.document.querySelector("#pin-tooltip").innerHTML = pined
? "Unpin"
: "Pin";
this._window.document
.querySelector("#pin")
.addEventListener("click", (e) => {
this.setPin();
});
this._window.addEventListener("keydown", (e) => {
// ctrl+w or esc
if ((e.key === "w" && e.ctrlKey) || e.keyCode === 27) {
this._window.close();
}
this.anchorPosition = {
left: img.scrollWidth / 2 - container.scrollLeft / 2,
top: img.scrollHeight / 2 - container.scrollLeft / 2,
};
if (e.keyCode === 37 || e.keyCode === 40) {
this.setIndex("left");
}
if (e.keyCode === 38 || e.keyCode === 39) {
this.setIndex("right");
}
if (e.key === "0") {
this.setScale(1);
} else if (e.keyCode === 107 || e.keyCode === 187 || e.key === "=") {
this.setScale(this.scaling * 1.1);
} else if (e.key === "-") {
this.setScale(this.scaling / 1.1);
}
});
this._window.addEventListener("wheel", async (e) => {
this.anchorPosition = {
left: e.pageX - container.offsetLeft,
top: e.pageY - container.offsetTop,
};
function normalizeWheelEventDirection(evt) {
let delta = Math.hypot(evt.deltaX, evt.deltaY);
const angle = Math.atan2(evt.deltaY, evt.deltaX);
if (-0.25 * Math.PI < angle && angle < 0.75 * Math.PI) {
// All that is left-up oriented has to change the sign.
delta = -delta;
}
return delta;
}
const delta = normalizeWheelEventDirection(e);
if (e.ctrlKey) {
this.setScale(
this.scaling *
Math.pow(delta > 0 ? 1.1 : 1 / 1.1, Math.round(Math.abs(delta)))
);
} else if (e.shiftKey) {
container.scrollLeft -= delta * 10;
} else {
container.scrollLeft += e.deltaX * 10;
container.scrollTop += e.deltaY * 10;
}
});
img.addEventListener("mousedown", (e) => {
e.preventDefault();
// if (this.scaling <= 1) {
// return;
// }
img.onmousemove = (e) => {
e.preventDefault();
container.scrollLeft -= e.movementX;
container.scrollTop -= e.movementY;
};
img.onmouseleave = () => {
img.onmousemove = null;
img.onmouseup = null;
};
img.onmouseup = () => {
img.onmousemove = null;
img.onmouseup = null;
};
});
}
this.srcList = srcs;
this.idx = idx;
this.title = title || "Note";
this.pined = pined;
this.setImage();
this.setScale(1);
this._window.focus();
}
private setImage() {
(this._window.document.querySelector("#image") as HTMLImageElement).src =
this.srcList[this.idx];
this.setTitle();
(
this._window.document.querySelector(
"#left-container"
) as HTMLButtonElement
).style.opacity = this.idx === 0 ? "0.5" : "1";
(
this._window.document.querySelector(
"#right-container"
) as HTMLButtonElement
).style.opacity = this.idx === this.srcList.length - 1 ? "0.5" : "1";
}
private setIndex(type: "left" | "right") {
if (type === "left") {
this.idx > 0 ? (this.idx -= 1) : undefined;
}
if (type === "right") {
this.idx < this.srcList.length - 1 ? (this.idx += 1) : undefined;
}
this.setImage();
}
private setScale(scaling: number) {
const oldScale = this.scaling;
this.scaling = scaling;
if (this.scaling > 10) {
this.scaling = 10;
}
if (this.scaling < 0.1) {
this.scaling = 0.1;
}
const container = this._window.document.querySelector(
".container"
) as HTMLDivElement;
(
this._window.document.querySelector("#image") as HTMLImageElement
).style.width = `calc(100% * ${this.scaling})`;
if (this.scaling > 1) {
container.scrollLeft +=
this.anchorPosition.left * (this.scaling - oldScale);
container.scrollTop +=
this.anchorPosition.top * (this.scaling - oldScale);
}
(
this._window.document.querySelector(
"#bigger-container"
) as HTMLButtonElement
).style.opacity = this.scaling === 10 ? "0.5" : "1";
(
this._window.document.querySelector(
"#smaller-container"
) as HTMLButtonElement
).style.opacity = this.scaling === 0.1 ? "0.5" : "1";
// (
// this._window.document.querySelector("#image") as HTMLImageElement
// ).style.cursor = this.scaling <= 1 ? "default" : "move";
}
private setTitle() {
this._window.document.querySelector("title").innerText = `${this.idx + 1}/${
this.srcList.length
}:${this.title}`;
}
private setPin() {
this._window.close();
this.onInit(this.srcList, this.idx, this.title, !this.pined);
}
}
export default EditorImageViewer;