您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
grab images from pages that you can't right click on images
- // ==UserScript==
- // @name Image Grabber
- // @namespace https://fxzfun.com/userscripts
- // @version 1.0.0
- // @description grab images from pages that you can't right click on images
- // @author FXZFun
- // @match *://*/*
- // @icon https://fxzfun.com/favicon.ico
- // @grant GM_addStyle
- // @license MIT
- // ==/UserScript==
- (function() {
- 'use strict';
- let elements;
- let overlay;
- let dialog;
- window.addEventListener('keypress', function(e){
- if (e.shiftKey && e.keyCode == 126) {
- startGrabber();
- }
- }, false);
- function isPointInBBox(x, y, bbox) {
- return ((x > bbox.left) &&
- (x < bbox.right) &&
- (y > bbox.top) &&
- (y < bbox.bottom))
- }
- function startGrabber() {
- GM_addStyle(`
- .fxzfun-ig-highlight { border: 1px solid darkred; }
- .fxzfun-ig-overlay { position: fixed; top: 0; left: 0; right: 0; bottom: 0; z-index: 1000;}
- dialog {
- width: 500px;
- margin: auto;
- border: none;
- border-radius: 12px;
- box-shadow: 0 0 5px #21212121;
- background-color: #fafafa;
- font-family: monospace, 'Roboto', 'Lato', 'Open Sans', sans-serif;
- outline: none;
- }
- dialog::backdrop { background: rgba(0, 0, 0, 0.2); }
- p.title {
- text-transform: uppercase;
- margin-top: 0;
- color: #757575;
- }
- .fxzfun-ig-gallery {
- display: flex;
- flex-wrap: wrap;
- gap: 20px;
- }
- .fxzfun-ig-gallery::empty {
- content: "No images found";
- }
- .fxzfun-ig-gallery img {
- width: 120px;
- height: 120px;
- border-radius: 6px;
- }
- .fxzfun-ig-gallery-item {
- display: flex;
- flex-direction: row;
- flex-wrap: wrap;
- gap: 20px;
- width: 100%;
- font-size: 1.2em;
- align-items: center;
- }
- .fxzfun-ig-gallery-item p {
- overflow: hidden;
- width: 300px;
- text-overflow: ellipsis;
- text-wrap: nowrap;
- }
- .fxzfun-ig-gallery-item div {
- flex-basis: 50%;
- }
- .fxzfun-ig-actions a {
- color: #757575;
- text-decoration: none;
- font-size: 16px;
- text-transform: uppercase;
- }
- .fxzfun-ig-actions a:hover {
- color: #bdbdbd;
- }
- `);
- overlay = document.createElement("div");
- overlay.classList.add("fxzfun-ig-overlay");
- document.body.appendChild(overlay);
- elements = new Set();
- document.body.addEventListener("mousemove", mouseMoveListener);
- overlay.addEventListener("click", e => {
- let images = new Set();
- elements.forEach(el => {
- let style = (el.currentStyle || window.getComputedStyle(el, false));
- if (el.tagName === "IMG") images.add(el.src);
- if (el.tagName === "SVG") images.add(URL.createObjectURL(new Blob([new XMLSerializer().serializeToString(el)], { type: 'image/svg+xml' })));
- if (style.backgroundImage.startsWith('data:image') || style.background.startsWith('data:image')) images.add(style.background);
- let backgroundImageMatches = style.backgroundImage.match(/url\(["']?(.*?)["']?\)/);
- if (backgroundImageMatches?.length > 1) images.add(backgroundImageMatches?.[1]);
- let backgroundMatches = style.backgroundImage.match(/url\(["']?(.*?)["']?\)/);
- if (backgroundMatches?.length > 1) images.add(backgroundMatches?.[1]);
- });
- showDialog(images);
- });
- }
- function mouseMoveListener(e) {
- document.querySelectorAll('*').forEach(node => {
- if (isPointInBBox(e.x, e.y, node.getBoundingClientRect())) {
- if (!node.classList.contains("fxzfun-ig-highlight") && !node.classList.contains("fxzfun-ig-overlay")) {
- node.classList.add("fxzfun-ig-highlight");
- elements.add(node);
- }
- } else {
- node.classList.remove("fxzfun-ig-highlight");
- elements.delete(node);
- }
- });
- }
- function showDialog(images) {
- dialog = dialog ?? document.createElement("dialog");
- let imageLinks = Array.from(images);
- dialog.innerHTML = `
- <p class="title">[/] Image Grabber</p>
- <div class="fxzfun-ig-gallery">
- ${imageLinks.map((url, index) => !!url && `
- <div class="fxzfun-ig-gallery-item" id="item${index}">
- <img src="${url}" />
- <div>
- <p>${url.split("/").at(-1)}</p>
- <p class="fxzfun-ig-actions">
- <a href="${url}" target="_blank">Open ${NEW_TAB_ICON}</a>
- <a href="${url}" target="_blank" download="${url.split("/").at(-1)}">Download ${DOWNLOAD_ICON}</a>
- </p>
- </div>
- </div>
- `).join('')}
- </div>`;
- document.body.appendChild(dialog);
- dialog.showModal();
- dialog.addEventListener('click', (e) => e.target.isEqualNode(dialog) && dialog.close());
- overlay.remove();
- document.querySelectorAll(".fxzfun-ig-highlight").forEach(el => el.classList.remove("fxzfun-ig-highlight"));
- document.querySelectorAll(".fxzfun-ig-copyLink").forEach(el => el);
- document.body.removeEventListener("mousemove", mouseMoveListener);
- }
- const NEW_TAB_ICON = `<svg width="16" height="16" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" d="M17 17H3V3h5V1H3a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-5h-2z"/><path fill="currentColor" d="m11 1l3.3 3.3L8.6 10l1.4 1.4l5.7-5.7L19 9V1z"/></svg>`;
- const DOWNLOAD_ICON = `<svg width="16" height="16" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" d="M17 12v5H3v-5H1v5a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-5z"/><path fill="currentColor" d="M15 9h-4V1H9v8H5l5 6z"/></svg>`;
- const COPY_ICON = `<svg width="16" height="16" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" d="M17 3H9v2H7V3c0-1.1.895-2 2-2h8c1.1 0 2 .895 2 2v8c0 1.1-.895 2-2 2h-2v-2h2z"/><path fill="currentColor" d="M3 9v8h8V9zm8-2c1.1 0 2 .895 2 2v8c0 1.1-.895 2-2 2H3c-1.1 0-2-.895-2-2V9c0-1.1.895-2 2-2z"/></svg>`;
- })();