Skip to content

Commit

Permalink
Add: view the source element (#360)
Browse files Browse the repository at this point in the history
* Add: tab monitor

* Add: view source element

* refactor
  • Loading branch information
eight04 authored Nov 19, 2024
1 parent 7e90276 commit 3272660
Show file tree
Hide file tree
Showing 7 changed files with 165 additions and 10 deletions.
16 changes: 14 additions & 2 deletions src/background.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import browser from "webextension-polyfill";

import {pref} from "./lib/pref.js";
import {createTab} from "./lib/tab.js";
import {tabMonitor} from "./lib/tab-monitor.js";
import {fetchImage} from "./lib/fetch-image.js";
import {download} from "./lib/downloader.js";
import {imageCache} from "./lib/image-cache.js";
Expand Down Expand Up @@ -143,8 +144,17 @@ const MENU_OPTIONS = [
handler(tab, info.frameId);
},
contexts: ["page", "image"],
oncontext: () => pref.get("contextMenu")
}))
oncontext: () => pref.get("contextMenu") && !tabMonitor.isExtensionPage()
})),
{
title: browser.i18n.getMessage("commandViewSourceElement"),
onclick(info, tab) {
browser.tabs.sendMessage(tab.id, {method: "viewSourceElementClicked", elementId: info.targetElementId})
.catch(notifyError);
},
contexts: ["image"],
oncontext: () => tabMonitor.isExtensionPage()
}
];

let menus;
Expand All @@ -166,6 +176,7 @@ if (menus) {
}
});
});
tabMonitor.on("change", () => menus.update());
}

// setup dynamic icon
Expand Down Expand Up @@ -401,6 +412,7 @@ function pickImagesToRightNoCurrent(tab) {
}

function notifyError(err) {
console.error(err);
browser.notifications.create({
type: "basic",
title: "Image Picka",
Expand Down
16 changes: 14 additions & 2 deletions src/content.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,22 @@ browser.runtime.onMessage.addListener(message => {
});
case "revokeURL":
return URL.revokeObjectURL(message.url);
case "viewSourceElement":
return viewSourceElement(message.pickaId);
}
});

initDragndrop({downloadImage});

function viewSourceElement(pickaId) {
const el = document.querySelector(`[data-picka-id="${pickaId}"]`);
if (!el) {
console.error(`Element with pickaId ${pickaId} not found`);
return;
}
el.scrollIntoView({behavior: "smooth"});
}

function downloadImage({url, referrerPolicy = getDefaultReferrerPolicy(), alt}) {
url = transformURL(url);
browser.runtime.sendMessage({
Expand All @@ -44,12 +55,13 @@ function downloadImage({url, referrerPolicy = getDefaultReferrerPolicy(), alt})

function getImages() {
const images = new Map;
for (const {src, referrerPolicy, alt} of getAllImages()) {
for (const {src, referrerPolicy, alt, pickaId} of getAllImages()) {
const url = transformURL(src)
const image = {
url,
referrer: getReferrer(location.href, url, referrerPolicy || getDefaultReferrerPolicy()),
alt
alt,
pickaId
};
const old = images.get(image.url);
if (old && cmpInfo(old, image) >= 0) {
Expand Down
7 changes: 6 additions & 1 deletion src/lib/image-util.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {parseSrcset} from "srcset";
import {pref} from "./pref.js";

let SRC_PROP = [];
let PICKA_ID = 1;
update();
pref.on("change", change => {
if (change.srcAlternative != null) {
Expand Down Expand Up @@ -73,10 +74,14 @@ export function *getAllImages() {
if (!src || /^[\w]+-extension/.test(src) || /^about/.test(src)) {
continue;
}
if (!el.dataset.pickaId) {
el.dataset.pickaId = PICKA_ID++;
}
yield {
src,
referrerPolicy: el.referrerPolicy,
alt: el.alt
alt: el.alt,
pickaId: el.dataset.pickaId,
};
}
}
Expand Down
67 changes: 67 additions & 0 deletions src/lib/tab-monitor.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// observee the latest active tab
import browser from "webextension-polyfill";
import EventLite from "event-lite";

class TabMonitor extends EventLite {
constructor() {
super();
this._tabId = null;
this._tab = null;
this._onActivated = this._onActivated.bind(this);
this._onUpdated = this._onUpdated.bind(this);
// FIXME: make it work with multiple windows
// NOTE: in Chrome MV2, these requires the "tabs" permission
browser.tabs.onActivated.addListener(this._onActivated);
try {
browser.tabs.onUpdated.addListener(this._onUpdated, {properties: ["url"]});
} catch (err) {
if (/filters/.test(err.message)) {
// Chrome doesn't support filters
browser.tabs.onUpdated.addListener(this._onUpdated);
}
}
}

_onActivated({tabId}) {
this._tabId = tabId;
browser.tabs.get(tabId)
.then(tab => {
this._onUpdated(tabId, null, tab);
});
}

_onUpdated(tabId, changeInfo, tab) {
if (!this._tabId || tabId !== this._tabId) {
return;
}
if (!tab.url) {
if (tab.finalUrl) {
tab.url = tab.finalUrl;
} else {
console.warn(`Tab ${tabId} has no url`);
return;
}
}
this._tab = tab;
this.emit("change");
}

getTab() {
return this._tab;
}

destroy() {
browser.tabs.onActivated.removeListener(this._onActivated);
browser.tabs.onUpdated.removeListener(this._onUpdated);
}

isExtensionPage() {
if (!this._tab) {
return false;
}
return this._tab.url.startsWith(browser.runtime.getURL(""));
}

}

export const tabMonitor = new TabMonitor();
64 changes: 59 additions & 5 deletions src/picker.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,13 @@ createCustomCSS();
const BATCH_ID = getBatchId();
const loadLock = createLockPool({maxActiveReader: 3});

let picker;

browser.runtime.sendMessage({method: "getBatchData", batchId: BATCH_ID})
.then(req => pref.ready().then(() => init(req)));
.then(async req => {
await pref.ready();
picker = init(req);
});

// toolbar expand
for (const el of document.querySelectorAll(".toolbar")) {
Expand All @@ -40,6 +45,37 @@ for (const input of document.querySelectorAll(".history-container input")) {
setupHistory(input, input.id);
}

let lastContextTarget;
document.addEventListener("contextmenu", e => {
lastContextTarget = e.target;
});

browser.runtime.onMessage.addListener(req => {
if (req.method === "viewSourceElementClicked") {
return onViewSourceElementClicked(req);
}
});

async function onViewSourceElementClicked(req) {
let element;
if (req.elementId) {
element = browser.menus.getTargetElement(req.elementId);
} else {
element = lastContextTarget;
}
if (!element) {
throw new Error("No element found");
}
const imageCheck = picker.coverToCheck(element);
await Promise.all([
browser.tabs.update(imageCheck.tabId, {active: true}),
browser.tabs.sendMessage(imageCheck.tabId, {
method: "viewSourceElement",
pickaId: imageCheck.pickaId
}, {frameId: imageCheck.frameId})
]);
}

function getBatchId() {
const id = new URL(location.href).searchParams.get("batchId");
return +id;
Expand All @@ -52,9 +88,12 @@ function init({tabs: originalTabs, env}) {
({
tabId: tab.tabId,
images: [].concat(...tab.frames.map(f => f.images.map(
({url, referrer, alt}) => {
// console.log(alt);
const check = createImageCheckbox(url, f.frameId, tab.tabId, referrer, alt);
(imageData) => {
const check = createImageCheckbox({
frameId: f.frameId,
tabId: tab.tabId,
...imageData
});
if (!pref.get("selectByDefault")) {
check.toggleCheck();
}
Expand Down Expand Up @@ -168,6 +207,8 @@ function init({tabs: originalTabs, env}) {
for (var [cls, cb] of Object.entries(handler)) {
actions.querySelector(`.${cls}`).onclick = cb;
}

return {coverToCheck};

function getUrls(images) {
return images.filter(i => i.selected()).map(i => i.url);
Expand All @@ -176,6 +217,15 @@ function init({tabs: originalTabs, env}) {
function getImages() {
return [].concat(...tabs.map(t => t.images));
}

function coverToCheck(cover) {
for (const image of getImages()) {
if (image.cover === cover) {
return image;
}
}
throw new Error("Failed converting cover to check");
}
}

function findSelectedImage(tabs) {
Expand Down Expand Up @@ -300,7 +350,7 @@ function initFilter(container, images) {
}
}

function createImageCheckbox(url, frameId, tabId, referrer, alt) {
function createImageCheckbox({url, frameId, tabId, referrer, alt, pickaId}) {
const label = document.createElement("label");
const input = document.createElement("input");
let ctrl;
Expand Down Expand Up @@ -361,7 +411,11 @@ function createImageCheckbox(url, frameId, tabId, referrer, alt) {
url,
data: null,
alt,
pickaId,
el: label,
cover: imgCover,
tabId,
frameId,
toggleEnable(enable) {
label.classList.toggle("disabled", !enable);
input.disabled = !enable;
Expand Down
4 changes: 4 additions & 0 deletions src/static/_locales/en/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@
"message": "Pick Images from Right Tabs",
"description": "'Pick images from right tabs' command"
},
"commandViewSourceElement": {
"message": "View source element",
"description": "'View source element' command in image picker"
},
"extensionName": {
"message": "Image Picka",
"description": "Name of the extension"
Expand Down
1 change: 1 addition & 0 deletions src/static/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"contextMenus",
"activeTab",
"notifications",
"tabs",
"<all_urls>"
],
"optional_permissions": [
Expand Down

0 comments on commit 3272660

Please sign in to comment.