Initial commit
This commit is contained in:
616
.obsidian/plugins/mousewheel-image-zoom/main.js
vendored
Normal file
616
.obsidian/plugins/mousewheel-image-zoom/main.js
vendored
Normal file
@@ -0,0 +1,616 @@
|
||||
/*
|
||||
THIS IS A GENERATED/BUNDLED FILE BY ROLLUP
|
||||
if you want to view the source visit the plugins github repository
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var obsidian = require('obsidian');
|
||||
|
||||
/*! *****************************************************************************
|
||||
Copyright (c) Microsoft Corporation.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
PERFORMANCE OF THIS SOFTWARE.
|
||||
***************************************************************************** */
|
||||
|
||||
function __awaiter(thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* ReplaceTerm enables us to store the parameters for a replacement to add a new size parameter.
|
||||
*/
|
||||
class ReplaceTerm {
|
||||
constructor(replaceFrom, replaceWith) {
|
||||
this.replaceFrom = replaceFrom;
|
||||
this.replaceWith = replaceWith;
|
||||
}
|
||||
// Generate a string that can be used in a string.replace() call as the string to replace
|
||||
getReplaceFromString(oldSize) {
|
||||
return this.replaceFrom(oldSize);
|
||||
}
|
||||
// Generate a string that can be used in a string.replace() call as the replacement string
|
||||
getReplaceWithString(newSize) {
|
||||
return this.replaceWith(newSize);
|
||||
}
|
||||
}
|
||||
class Util {
|
||||
/**
|
||||
* For a given file content decide if a string is inside a table
|
||||
* @param searchString string
|
||||
* @param fileValue file content
|
||||
* @private
|
||||
*/
|
||||
static isInTable(searchString, fileValue) {
|
||||
return fileValue.search(new RegExp(`^\\|.+${escapeRegex(searchString)}.+\\|$`, "m")) !== -1;
|
||||
}
|
||||
/**
|
||||
* Get the image name from a given src uri of a local image
|
||||
* (URI like app://local/C:/.../image.png?1677337704730)
|
||||
* @param imageUri uri of the image
|
||||
* @private
|
||||
*/
|
||||
static getLocalImageNameFromUri(imageUri) {
|
||||
imageUri = decodeURI(imageUri);
|
||||
const imageNameMatch = imageUri.match(/([^\/?\\]+)(\?.*?|)$/);
|
||||
const imageName = imageNameMatch ? imageNameMatch[1] : "";
|
||||
// Handle linux not correctly decoding the %2F before the Filename to a \
|
||||
const hasLinuxDecodingIssue = imageName.startsWith("2F");
|
||||
return hasLinuxDecodingIssue ? imageName.slice(2) : imageName;
|
||||
}
|
||||
/**
|
||||
* Get the parameters needed to handle the zoom for a local image.
|
||||
* Source can be either a obsidian link like [[image.png]] or a markdown link like [image.png](image.png)
|
||||
* @param imageName Name of the image
|
||||
* @param fileText content of the current file
|
||||
* @returns parameters to handle the zoom
|
||||
*/
|
||||
static getLocalImageZoomParams(imageName, fileText) {
|
||||
imageName = this.determineImageName(imageName, fileText);
|
||||
// Get the folder name if the image is located in a folder
|
||||
const folderName = this.getFolderNameIfExist(imageName, fileText);
|
||||
imageName = `${folderName}${imageName}`;
|
||||
const isInTable = Util.isInTable(imageName, fileText);
|
||||
// Separator to use for the replacement
|
||||
const sizeSeparator = isInTable ? "\\|" : "|";
|
||||
// Separator to use for the regex: isInTable ? \\\| : \|
|
||||
const regexSeparator = isInTable ? "\\\\\\|" : "\\|";
|
||||
// check character before the imageName to check if markdown link or obsidian link
|
||||
const imageNamePosition = fileText.indexOf(imageName);
|
||||
const isObsidianLink = fileText.charAt(imageNamePosition - 1) === "[";
|
||||
if (isObsidianLink) {
|
||||
const imageAttributes = this.getImageAttributes(imageName, fileText);
|
||||
imageName = `${imageName}${imageAttributes}`;
|
||||
return Util.generateReplaceTermForObsidianSyntax(imageName, regexSeparator, sizeSeparator);
|
||||
}
|
||||
else {
|
||||
return Util.generateReplaceTermForMarkdownSyntax(imageName, regexSeparator, sizeSeparator, fileText);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* When using markdown link syntax the image name can be encoded. This function checks if the image name is encoded and if not encodes it.
|
||||
*
|
||||
* @param origImageName Image name
|
||||
* @param fileText File content
|
||||
* @returns image name with the correct encoding
|
||||
*/
|
||||
static determineImageName(origImageName, fileText) {
|
||||
const encodedImageName = encodeURI(origImageName);
|
||||
const spaceEncodedImageName = origImageName.replace(/ /g, "%20");
|
||||
// Try matching original, full URI encoded, and space encoded
|
||||
const imageNameVariants = [origImageName, encodedImageName, spaceEncodedImageName];
|
||||
for (const variant of imageNameVariants) {
|
||||
if (fileText.includes(variant)) {
|
||||
return variant;
|
||||
}
|
||||
}
|
||||
throw new Error("Image not found in file");
|
||||
}
|
||||
/**
|
||||
* Extracts the folder name from the given image name by looking for the first "[" or "(" character
|
||||
* that appears before the image name in the file text.
|
||||
* @param imageName The name of the image.
|
||||
* @param fileText The text of the file that contains the image.
|
||||
* @returns The name of the folder that contains the image, or an empty string if no folder is found.
|
||||
*/
|
||||
static getFolderNameIfExist(imageName, fileText) {
|
||||
const index = fileText.indexOf(imageName);
|
||||
if (index === -1) {
|
||||
throw new Error("Image not found in file");
|
||||
}
|
||||
const stringBeforeFileName = fileText.substring(0, index);
|
||||
const lastOpeningBracket = stringBeforeFileName.lastIndexOf("["); // Obsidian link
|
||||
const lastOpeningParenthesis = stringBeforeFileName.lastIndexOf("("); // Markdown link
|
||||
const lastOpeningBracketOrParenthesis = Math.max(lastOpeningBracket, lastOpeningParenthesis);
|
||||
const folderName = stringBeforeFileName.substring(lastOpeningBracketOrParenthesis + 1);
|
||||
return folderName;
|
||||
}
|
||||
/**
|
||||
* Extracts any image attributes like |ctr for ITS Theme that appear after the given image name in the file.
|
||||
* @param imageName - The name of the image to search for.
|
||||
* @param fileText - The content of the file to search in.
|
||||
* @returns A string containing any image attributes that appear after the image name.
|
||||
*/
|
||||
static getImageAttributes(imageName, fileText) {
|
||||
const index = fileText.indexOf(imageName);
|
||||
const stringAfterFileName = fileText.substring(index + imageName.length);
|
||||
const regExpMatchArray = stringAfterFileName.match(/([^\]]*?)\\?\|\d+]]|([^\]]*?)]]|/);
|
||||
if (regExpMatchArray) {
|
||||
if (!!regExpMatchArray[1]) {
|
||||
return regExpMatchArray[1];
|
||||
}
|
||||
else if (!!regExpMatchArray[2]) {
|
||||
return regExpMatchArray[2];
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
/**
|
||||
* Get the parameters needed to handle the zoom for images in markdown format.
|
||||
* Example: 
|
||||
* @param imageName Name of the image
|
||||
* @param fileText content of the current file
|
||||
* @returns parameters to handle the zoom
|
||||
* @private
|
||||
*
|
||||
*/
|
||||
static generateReplaceTermForMarkdownSyntax(imageName, regexSeparator, sizeSeparator, fileText) {
|
||||
const sizeMatchRegExp = new RegExp(`${regexSeparator}(\\d+)]${escapeRegex("(" + imageName + ")")}`);
|
||||
const replaceSizeExistFrom = (oldSize) => `${sizeSeparator}${oldSize}](${imageName})`;
|
||||
const replaceSizeExistWith = (newSize) => `${sizeSeparator}${newSize}](${imageName})`;
|
||||
const replaceSizeNotExistsFrom = (oldSize) => `](${imageName})`;
|
||||
const replaceSizeNotExistsWith = (newSize) => `${sizeSeparator}${newSize}](${imageName})`;
|
||||
const replaceSizeExist = new ReplaceTerm(replaceSizeExistFrom, replaceSizeExistWith);
|
||||
const replaceSizeNotExist = new ReplaceTerm(replaceSizeNotExistsFrom, replaceSizeNotExistsWith);
|
||||
return {
|
||||
sizeMatchRegExp: sizeMatchRegExp,
|
||||
replaceSizeExist: replaceSizeExist,
|
||||
replaceSizeNotExist: replaceSizeNotExist,
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Get the parameters needed to handle the zoom for images in markdown format.
|
||||
* Example: ![[image.png]]
|
||||
* @param imageName Name of the image
|
||||
* @param fileText content of the current file
|
||||
* @returns parameters to handle the zoom
|
||||
* @private
|
||||
*
|
||||
*/
|
||||
static generateReplaceTermForObsidianSyntax(imageName, regexSeparator, sizeSeparator) {
|
||||
const sizeMatchRegExp = new RegExp(`${escapeRegex(imageName)}${regexSeparator}(\\d+)`);
|
||||
const replaceSizeExistFrom = (oldSize) => `${imageName}${sizeSeparator}${oldSize}`;
|
||||
const replaceSizeExistWith = (newSize) => `${imageName}${sizeSeparator}${newSize}`;
|
||||
const replaceSizeNotExistsFrom = (oldSize) => `${imageName}`;
|
||||
const replaceSizeNotExistsWith = (newSize) => `${imageName}${sizeSeparator}${newSize}`;
|
||||
const replaceSizeExist = new ReplaceTerm(replaceSizeExistFrom, replaceSizeExistWith);
|
||||
const replaceSizeNotExist = new ReplaceTerm(replaceSizeNotExistsFrom, replaceSizeNotExistsWith);
|
||||
return {
|
||||
sizeMatchRegExp: sizeMatchRegExp,
|
||||
replaceSizeExist: replaceSizeExist,
|
||||
replaceSizeNotExist: replaceSizeNotExist,
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Get the parameters needed to handle the zoom for a remote image.
|
||||
* Format: https://www.example.com/image.png
|
||||
* @param imageUri URI of the image
|
||||
* @param fileText content of the current file
|
||||
* @returns parameters to handle the zoom
|
||||
*/
|
||||
static getRemoteImageZoomParams(imageUri, fileText) {
|
||||
const isInTable = Util.isInTable(imageUri, fileText);
|
||||
// Separator to use for the replacement
|
||||
const sizeSeparator = isInTable ? "\\|" : "|";
|
||||
// Separator to use for the regex: isInTable ? \\\| : \|
|
||||
const regexSeparator = isInTable ? "\\\\\\|" : "\\|";
|
||||
return Util.generateReplaceTermForMarkdownSyntax(imageUri, regexSeparator, sizeSeparator, fileText);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Function to escape a string into a valid searchable string for a regex
|
||||
* @param string string to escape
|
||||
* @returns escaped string
|
||||
*/
|
||||
function escapeRegex(string) {
|
||||
return string.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
|
||||
}
|
||||
|
||||
var ModifierKey;
|
||||
(function (ModifierKey) {
|
||||
ModifierKey["ALT"] = "AltLeft";
|
||||
ModifierKey["CTRL"] = "ControlLeft";
|
||||
ModifierKey["SHIFT"] = "ShiftLeft";
|
||||
ModifierKey["ALT_RIGHT"] = "AltRight";
|
||||
ModifierKey["CTRL_RIGHT"] = "ControlRight";
|
||||
ModifierKey["SHIFT_RIGHT"] = "ShiftRight";
|
||||
})(ModifierKey || (ModifierKey = {}));
|
||||
const DEFAULT_SETTINGS = {
|
||||
modifierKey: ModifierKey.ALT,
|
||||
stepSize: 25,
|
||||
initialSize: 500,
|
||||
resizeInCanvas: true,
|
||||
};
|
||||
const CtrlCanvasConflictWarning = "Warning: Using Ctrl as the modifier key conflicts with default canvas zooming behavior when 'Resize in canvas' is enabled. Consider using another modifier key or disabling 'Resize in canvas'.";
|
||||
class MouseWheelZoomPlugin extends obsidian.Plugin {
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
this.isKeyHeldDown = false;
|
||||
this.wheelOpt = { passive: false, capture: true };
|
||||
this.wheelEvent = 'wheel';
|
||||
}
|
||||
onload() {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
yield this.loadSettings();
|
||||
this.registerEvent(this.app.workspace.on("window-open", (newWindow) => this.registerEvents(newWindow.win)));
|
||||
this.registerEvents(window);
|
||||
this.addSettingTab(new MouseWheelZoomSettingsTab(this.app, this));
|
||||
console.log("Loaded: Mousewheel image zoom");
|
||||
this.checkExistingUserConflict();
|
||||
});
|
||||
}
|
||||
checkExistingUserConflict() {
|
||||
const noticeShownKey = 'mousewheel-zoom-ctrl-warning-shown'; // Key for localStorage flag
|
||||
const isCtrl = this.settings.modifierKey === ModifierKey.CTRL || this.settings.modifierKey === ModifierKey.CTRL_RIGHT;
|
||||
// Only show the notice if the conflict exists AND the user hasn't dismissed it before (using localStorage flag)
|
||||
if (isCtrl && this.settings.resizeInCanvas && !localStorage.getItem(noticeShownKey)) {
|
||||
const fragment = document.createDocumentFragment();
|
||||
const titleEl = document.createElement('strong');
|
||||
titleEl.textContent = "Mousewheel Image Zoom";
|
||||
fragment.appendChild(titleEl);
|
||||
fragment.appendChild(document.createElement('br'));
|
||||
const messageEl = document.createElement('span');
|
||||
messageEl.textContent = CtrlCanvasConflictWarning;
|
||||
fragment.appendChild(messageEl);
|
||||
fragment.appendChild(document.createElement('br'));
|
||||
const settingsButton = document.createElement('button');
|
||||
settingsButton.textContent = "Open Settings";
|
||||
settingsButton.style.marginTop = "5px";
|
||||
settingsButton.onclick = () => {
|
||||
// settings is a private property of the app object, so we need to cast it to any to access it
|
||||
// See https://forum.obsidian.md/t/open-settings-for-my-plugin-community-plugin-settings-deeplink/61563/4
|
||||
const setting = this.app.setting;
|
||||
setting.open();
|
||||
setting.openTabById(this.manifest.id);
|
||||
};
|
||||
fragment.appendChild(settingsButton);
|
||||
new obsidian.Notice(fragment, 0);
|
||||
// Set the flag in localStorage so the notice doesn't appear again
|
||||
// unless the user clears their localStorage or the key changes.
|
||||
localStorage.setItem(noticeShownKey, 'true');
|
||||
}
|
||||
}
|
||||
/**
|
||||
* When the config key is released, we enable the scroll again and reset the key held down flag.
|
||||
*/
|
||||
onConfigKeyUp(currentWindow) {
|
||||
this.isKeyHeldDown = false;
|
||||
this.enableScroll(currentWindow);
|
||||
}
|
||||
onunload(currentWindow = window) {
|
||||
// Re-enable the normal scrolling behavior when the plugin unloads
|
||||
this.enableScroll(currentWindow);
|
||||
}
|
||||
/**
|
||||
* Registers image resizing events for the specified window
|
||||
* @param currentWindow window in which to register events
|
||||
* @private
|
||||
*/
|
||||
registerEvents(currentWindow) {
|
||||
const doc = currentWindow.document;
|
||||
this.registerDomEvent(doc, "keydown", (evt) => {
|
||||
var _a;
|
||||
if (evt.code === this.settings.modifierKey.toString()) {
|
||||
// When canvas mode is enabled we just ignore the keydown event if the canvas is active
|
||||
const isActiveViewCanvas = ((_a = this.app.workspace.getActiveViewOfType(obsidian.View)) === null || _a === void 0 ? void 0 : _a.getViewType()) === "canvas";
|
||||
if (isActiveViewCanvas && !this.settings.resizeInCanvas) {
|
||||
return;
|
||||
}
|
||||
this.isKeyHeldDown = true;
|
||||
if (this.settings.modifierKey !== ModifierKey.SHIFT && this.settings.modifierKey !== ModifierKey.SHIFT_RIGHT) { // Ignore shift to allow horizontal scrolling
|
||||
// Disable the normal scrolling behavior when the key is held down
|
||||
this.disableScroll(currentWindow);
|
||||
}
|
||||
}
|
||||
});
|
||||
this.registerDomEvent(doc, "keyup", (evt) => {
|
||||
if (evt.code === this.settings.modifierKey.toString()) {
|
||||
this.onConfigKeyUp(currentWindow);
|
||||
}
|
||||
});
|
||||
this.registerDomEvent(doc, "wheel", (evt) => {
|
||||
if (this.isKeyHeldDown) {
|
||||
// When for example using Alt + Tab to switch between windows, the key is still recognized as held down.
|
||||
// We check if the key is really held down by checking if the key is still pressed in the event when the
|
||||
// wheel event is triggered.
|
||||
if (!this.isConfiguredKeyDown(evt)) {
|
||||
this.onConfigKeyUp(currentWindow);
|
||||
return;
|
||||
}
|
||||
const eventTarget = evt.target;
|
||||
const targetIsCanvas = eventTarget.hasClass("canvas-node-content-blocker");
|
||||
const targetIsCanvasNode = eventTarget.closest(".canvas-node-content") !== null;
|
||||
const targetIsImage = eventTarget.nodeName === "IMG";
|
||||
if (targetIsCanvas || targetIsCanvasNode || targetIsImage) {
|
||||
this.disableScroll(currentWindow);
|
||||
}
|
||||
if (targetIsCanvas && this.settings.resizeInCanvas) {
|
||||
// seems we're trying to zoom on some canvas node.
|
||||
this.handleZoomForCanvas(evt, eventTarget);
|
||||
}
|
||||
else if (targetIsCanvasNode) ;
|
||||
else if (targetIsImage) {
|
||||
// Handle the zooming of the image
|
||||
this.handleZoom(evt, eventTarget);
|
||||
}
|
||||
}
|
||||
});
|
||||
this.registerDomEvent(currentWindow, "blur", () => {
|
||||
// When the window loses focus, ensure scrolling is re-enabled for this window
|
||||
// and reset the key held state defensively, although the keyup should ideally handle it.
|
||||
this.isKeyHeldDown = false;
|
||||
this.enableScroll(currentWindow);
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Handles zooming with the mousewheel on canvas node
|
||||
* @param evt wheel event
|
||||
* @param eventTarget targeted canvas node element
|
||||
* @private
|
||||
*/
|
||||
handleZoomForCanvas(evt, eventTarget) {
|
||||
// get active canvas
|
||||
const isCanvas = this.app.workspace.getActiveViewOfType(obsidian.View).getViewType() === "canvas";
|
||||
if (!isCanvas) {
|
||||
throw new Error("Can't find canvas");
|
||||
}
|
||||
// Unfortunately the current type definitions don't include any canvas functionality...
|
||||
const canvas = this.app.workspace.getActiveViewOfType(obsidian.View).canvas;
|
||||
// get triggered canvasNode
|
||||
const canvasNode = Array.from(canvas.nodes.values())
|
||||
.find(node => node.contentBlockerEl == eventTarget);
|
||||
// Adjust delta based on the direction of the resize
|
||||
let delta = evt.deltaY > 0 ? this.settings.stepSize : this.settings.stepSize * -1;
|
||||
// Calculate new dimensions directly using the delta and aspectRatio
|
||||
const aspectRatio = canvasNode.width / canvasNode.height;
|
||||
const newWidth = canvasNode.width + delta;
|
||||
const newHeight = newWidth / aspectRatio;
|
||||
// Resize the canvas node using the new dimensions
|
||||
canvasNode.resize({ width: newWidth, height: newHeight });
|
||||
}
|
||||
/**
|
||||
* Handles zooming with the mousewheel on an image
|
||||
* @param evt wheel event
|
||||
* @param eventTarget targeted image element
|
||||
* @private
|
||||
*/
|
||||
handleZoom(evt, eventTarget) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
const imageUri = eventTarget.attributes.getNamedItem("src").textContent;
|
||||
const activeFile = yield this.getActivePaneWithImage(eventTarget);
|
||||
yield this.app.vault.process(activeFile, (fileText) => {
|
||||
let frontmatter = "";
|
||||
let body = fileText;
|
||||
const frontmatterRegex = /^---\s*([\s\S]*?)\s*---\n*/;
|
||||
const match = fileText.match(frontmatterRegex);
|
||||
if (match) {
|
||||
frontmatter = match[0]; // Keep the full matched frontmatter block including delimiters and trailing newline
|
||||
body = fileText.slice(frontmatter.length); // The rest is the body
|
||||
}
|
||||
const zoomParams = this.getZoomParams(imageUri, body, eventTarget);
|
||||
// Perform replacements ONLY on the body
|
||||
let modifiedBody = body;
|
||||
const sizeMatches = body.match(zoomParams.sizeMatchRegExp);
|
||||
// Element already has a size entry in the body
|
||||
if (sizeMatches !== null) {
|
||||
const oldSize = parseInt(sizeMatches[1]);
|
||||
let newSize = oldSize;
|
||||
if (evt.deltaY < 0) {
|
||||
newSize += this.settings.stepSize;
|
||||
}
|
||||
else if (evt.deltaY > 0 && newSize > this.settings.stepSize) {
|
||||
newSize -= this.settings.stepSize;
|
||||
}
|
||||
// Replace within the body
|
||||
modifiedBody = body.replace(zoomParams.replaceSizeExist.getReplaceFromString(oldSize), zoomParams.replaceSizeExist.getReplaceWithString(newSize));
|
||||
}
|
||||
else { // Element has no size entry in the body -> give it an initial size
|
||||
const initialSize = this.settings.initialSize;
|
||||
const image = new Image();
|
||||
image.src = imageUri;
|
||||
const width = image.naturalWidth || initialSize;
|
||||
const minWidth = Math.min(width, initialSize);
|
||||
// Replace within the body
|
||||
modifiedBody = body.replace(zoomParams.replaceSizeNotExist.getReplaceFromString(0), zoomParams.replaceSizeNotExist.getReplaceWithString(minWidth));
|
||||
}
|
||||
// Combine original frontmatter with the modified body
|
||||
return frontmatter + modifiedBody;
|
||||
});
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Loop through all panes and get the pane that hosts a markdown file with the image to zoom
|
||||
* @param imageElement The HTML Element of the image
|
||||
* @private
|
||||
*/
|
||||
getActivePaneWithImage(imageElement) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
return new Promise(((resolve, reject) => {
|
||||
this.app.workspace.iterateAllLeaves(leaf => {
|
||||
if (leaf.view.containerEl.contains(imageElement) && leaf.view instanceof obsidian.MarkdownView) {
|
||||
resolve(leaf.view.file);
|
||||
}
|
||||
});
|
||||
reject(new Error("No file belonging to the image found"));
|
||||
}));
|
||||
});
|
||||
}
|
||||
getZoomParams(imageUri, fileText, target) {
|
||||
if (imageUri.contains("http")) {
|
||||
return Util.getRemoteImageZoomParams(imageUri, fileText);
|
||||
}
|
||||
else if (target.classList.value.match("excalidraw-svg.*")) {
|
||||
const src = target.attributes.getNamedItem("filesource").textContent;
|
||||
// remove ".md" from the end of the src
|
||||
const imageName = src.substring(0, src.length - 3);
|
||||
// Only get text after "/"
|
||||
const imageNameAfterSlash = imageName.substring(imageName.lastIndexOf("/") + 1);
|
||||
return Util.getLocalImageZoomParams(imageNameAfterSlash, fileText);
|
||||
}
|
||||
else if (imageUri.contains("app://")) {
|
||||
const imageName = Util.getLocalImageNameFromUri(imageUri);
|
||||
return Util.getLocalImageZoomParams(imageName, fileText);
|
||||
}
|
||||
else if (imageUri.contains("data:image/")) { // for image generated by PDF++ extension
|
||||
// example: data:image/png;base64,iVB...
|
||||
const imageName = Util.getLocalImageNameFromUri(target.parentElement.getAttribute("src"));
|
||||
return Util.getLocalImageZoomParams(imageName, fileText);
|
||||
}
|
||||
throw new Error("Image is not zoomable");
|
||||
}
|
||||
loadSettings() {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
this.settings = Object.assign({}, DEFAULT_SETTINGS, yield this.loadData());
|
||||
});
|
||||
}
|
||||
saveSettings() {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
yield this.saveData(this.settings);
|
||||
});
|
||||
}
|
||||
// Utilities to disable and enable scrolling //
|
||||
preventDefault(ev) {
|
||||
ev.preventDefault();
|
||||
}
|
||||
/**
|
||||
* Disables the normal scroll event
|
||||
*/
|
||||
disableScroll(currentWindow) {
|
||||
currentWindow.addEventListener(this.wheelEvent, this.preventDefault, this.wheelOpt);
|
||||
}
|
||||
/**
|
||||
* Enables the normal scroll event
|
||||
*/
|
||||
enableScroll(currentWindow) {
|
||||
currentWindow.removeEventListener(this.wheelEvent, this.preventDefault, this.wheelOpt);
|
||||
}
|
||||
isConfiguredKeyDown(evt) {
|
||||
switch (this.settings.modifierKey) {
|
||||
case ModifierKey.ALT:
|
||||
case ModifierKey.ALT_RIGHT:
|
||||
return evt.altKey;
|
||||
case ModifierKey.CTRL:
|
||||
case ModifierKey.CTRL_RIGHT:
|
||||
return evt.ctrlKey;
|
||||
case ModifierKey.SHIFT:
|
||||
case ModifierKey.SHIFT_RIGHT:
|
||||
return evt.shiftKey;
|
||||
}
|
||||
}
|
||||
}
|
||||
class MouseWheelZoomSettingsTab extends obsidian.PluginSettingTab {
|
||||
constructor(app, plugin) {
|
||||
super(app, plugin);
|
||||
this.plugin = plugin;
|
||||
}
|
||||
// Helper function to update the warning message
|
||||
updateWarningMessage(modifierKey, resizeInCanvas) {
|
||||
if (!this.warningEl)
|
||||
return;
|
||||
const isCtrl = modifierKey === ModifierKey.CTRL || modifierKey === ModifierKey.CTRL_RIGHT;
|
||||
const conflict = isCtrl && resizeInCanvas;
|
||||
if (conflict) {
|
||||
this.warningEl.setText(CtrlCanvasConflictWarning);
|
||||
this.warningEl.style.display = 'block';
|
||||
this.warningEl.style.color = 'var(--text-warning)';
|
||||
this.warningEl.style.marginTop = '10px';
|
||||
}
|
||||
else {
|
||||
this.warningEl.setText("");
|
||||
this.warningEl.style.display = 'none';
|
||||
}
|
||||
}
|
||||
display() {
|
||||
let { containerEl } = this;
|
||||
containerEl.empty();
|
||||
containerEl.createEl('h2', { text: 'Settings for mousewheel zoom' });
|
||||
new obsidian.Setting(containerEl)
|
||||
.setName('Trigger Key')
|
||||
.setDesc('Key that needs to be pressed down for mousewheel zoom to work.')
|
||||
.addDropdown(dropdown => dropdown
|
||||
.addOption(ModifierKey.CTRL, "Ctrl")
|
||||
.addOption(ModifierKey.ALT, "Alt")
|
||||
.addOption(ModifierKey.SHIFT, "Shift")
|
||||
.addOption(ModifierKey.CTRL_RIGHT, "Right Ctrl")
|
||||
.addOption(ModifierKey.ALT_RIGHT, "Right Alt")
|
||||
.addOption(ModifierKey.SHIFT_RIGHT, "Right Shift")
|
||||
.setValue(this.plugin.settings.modifierKey)
|
||||
.onChange((value) => __awaiter(this, void 0, void 0, function* () {
|
||||
this.plugin.settings.modifierKey = value;
|
||||
this.updateWarningMessage(this.plugin.settings.modifierKey, this.plugin.settings.resizeInCanvas);
|
||||
yield this.plugin.saveSettings();
|
||||
})));
|
||||
new obsidian.Setting(containerEl)
|
||||
.setName('Step size')
|
||||
.setDesc('Step value by which the size of the image should be increased/decreased')
|
||||
.addSlider(slider => {
|
||||
slider
|
||||
.setValue(25)
|
||||
.setLimits(0, 100, 1)
|
||||
.setDynamicTooltip()
|
||||
.setValue(this.plugin.settings.stepSize)
|
||||
.onChange((value) => __awaiter(this, void 0, void 0, function* () {
|
||||
this.plugin.settings.stepSize = value;
|
||||
yield this.plugin.saveSettings();
|
||||
}));
|
||||
});
|
||||
new obsidian.Setting(containerEl)
|
||||
.setName('Initial Size')
|
||||
.setDesc('Initial image size if no size was defined beforehand')
|
||||
.addSlider(slider => {
|
||||
slider
|
||||
.setValue(500)
|
||||
.setLimits(0, 1000, 25)
|
||||
.setDynamicTooltip()
|
||||
.setValue(this.plugin.settings.initialSize)
|
||||
.onChange((value) => __awaiter(this, void 0, void 0, function* () {
|
||||
this.plugin.settings.initialSize = value;
|
||||
yield this.plugin.saveSettings();
|
||||
}));
|
||||
});
|
||||
new obsidian.Setting(containerEl)
|
||||
.setName('Resize in canvas')
|
||||
.setDesc('When enabled, all nodes on the Obsidian canvas can also be resized using the Modifier key')
|
||||
.addToggle((toggle) => {
|
||||
toggle.setValue(this.plugin.settings.resizeInCanvas)
|
||||
.onChange((value) => __awaiter(this, void 0, void 0, function* () {
|
||||
this.plugin.settings.resizeInCanvas = value;
|
||||
this.updateWarningMessage(this.plugin.settings.modifierKey, value);
|
||||
yield this.plugin.saveSettings();
|
||||
}));
|
||||
});
|
||||
this.warningEl = containerEl.createDiv({ cls: 'mousewheel-zoom-warning' });
|
||||
this.warningEl.style.display = 'none';
|
||||
this.updateWarningMessage(this.plugin.settings.modifierKey, this.plugin.settings.resizeInCanvas);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = MouseWheelZoomPlugin;
|
||||
|
||||
|
||||
/* nosourcemap */
|
||||
Reference in New Issue
Block a user