Image Extract (Circle Gesture)

Trigger image extract popup by drawing a circle gesture

Bu betiği kurabilmeniz için Tampermonkey, Greasemonkey ya da Violentmonkey gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği yüklemek için Tampermonkey gibi bir uzantı yüklemeniz gerekir.

Bu betiği kurabilmeniz için Tampermonkey ya da Violentmonkey gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği kurabilmeniz için Tampermonkey ya da Userscripts gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği indirebilmeniz için ayrıca Tampermonkey gibi bir eklenti kurmanız gerekmektedir.

Bu komut dosyasını yüklemek için bir kullanıcı komut dosyası yöneticisi uzantısı yüklemeniz gerekecek.

(Zaten bir kullanıcı komut dosyası yöneticim var, kurmama izin verin!)

Bu stili yüklemek için Stylus gibi bir uzantı yüklemeniz gerekir.

Bu stili yüklemek için Stylus gibi bir uzantı kurmanız gerekir.

Bu stili yükleyebilmek için Stylus gibi bir uzantı yüklemeniz gerekir.

Bu stili yüklemek için bir kullanıcı stili yöneticisi uzantısı yüklemeniz gerekir.

Bu stili yüklemek için bir kullanıcı stili yöneticisi uzantısı kurmanız gerekir.

Bu stili yükleyebilmek için bir kullanıcı stili yöneticisi uzantısı yüklemeniz gerekir.

(Zateb bir user-style yöneticim var, yükleyeyim!)

// ==UserScript==
// @name         Image Extract (Circle Gesture)
// @namespace    CircleGestureImageExtract
// @version      1.0
// @description  Trigger image extract popup by drawing a circle gesture
// @match        *://*/*
// ==/UserScript==

(function() {
    'use strict';

    function createUI(images) {
        if (document.querySelector('#image-extract-panel')) return;

        const panel = document.createElement('div');
        panel.id = 'image-extract-panel';
        panel.style.position = 'fixed';
        panel.style.top = '10px';
        panel.style.left = '10px';
        panel.style.background = '#222';
        panel.style.color = '#eee';
        panel.style.border = '1px solid #555';
        panel.style.padding = '10px';
        panel.style.zIndex = 999999;
        panel.style.maxHeight = '400px';
        panel.style.overflowY = 'auto';
        panel.style.width = '300px';

        const closeBtn = document.createElement('span');
        closeBtn.textContent = '×';
        closeBtn.style.cursor = 'pointer';
        closeBtn.style.float = 'right';
        closeBtn.style.fontSize = '18px';
        closeBtn.style.fontWeight = 'bold';
        closeBtn.onclick = () => panel.remove();
        panel.appendChild(closeBtn);

        const title = document.createElement('div');
        title.textContent = `Found ${images.length} images`;
        title.style.fontWeight = 'bold';
        panel.appendChild(title);

        const list = document.createElement('div');
        images.forEach(src => {
            const thumb = document.createElement('img');
            thumb.src = src;
            thumb.style.maxWidth = '100%';
            thumb.style.margin = '5px 0';
            list.appendChild(thumb);
        });
        panel.appendChild(list);

        document.body.appendChild(panel);
    }

    function extractImages() {
        const imgs = Array.from(document.querySelectorAll('img'))
            .map(img => img.src)
            .filter(src => src && src.startsWith('http'));

        const bgImgs = Array.from(document.querySelectorAll('*'))
            .map(el => getComputedStyle(el).backgroundImage)
            .filter(bg => bg && bg.startsWith('url'))
            .map(bg => bg.replace(/^url\(["']?/, '').replace(/["']?\)$/, ''));

        const allImgs = [...imgs, ...bgImgs];
        if (allImgs.length > 0) {
            createUI(allImgs);
        } else {
            alert("No images found!");
        }
    }

    // Gesture detection
    let gesturePoints = [];
    let canvas, ctx;

    function initCanvas() {
        if (canvas) return;
        canvas = document.createElement('canvas');
        canvas.style.position = 'fixed';
        canvas.style.top = 0;
        canvas.style.left = 0;
        canvas.width = window.innerWidth;
        canvas.height = window.innerHeight;
        canvas.style.zIndex = 999998;
        canvas.style.pointerEvents = 'none';
        document.body.appendChild(canvas);
        ctx = canvas.getContext('2d');
    }

    function drawPath(points) {
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        ctx.strokeStyle = 'rgba(0,255,0,0.5)';
        ctx.lineWidth = 3;
        ctx.beginPath();
        ctx.moveTo(points[0][0], points[0][1]);
        for (let i = 1; i < points.length; i++) {
            ctx.lineTo(points[i][0], points[i][1]);
        }
        ctx.stroke();
    }

    document.addEventListener('touchstart', e => {
        initCanvas();
        gesturePoints = [];
        const touch = e.touches[0];
        gesturePoints.push([touch.clientX, touch.clientY]);
    });

    document.addEventListener('touchmove', e => {
        const touch = e.touches[0];
        gesturePoints.push([touch.clientX, touch.clientY]);
        drawPath(gesturePoints);
    });

    document.addEventListener('touchend', () => {
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        if (isCircleGesture(gesturePoints)) {
            extractImages();
        }
    });

    function isCircleGesture(points) {
        if (points.length < 6) return false;

        const xs = points.map(p => p[0]);
        const ys = points.map(p => p[1]);
        const w = Math.max(...xs) - Math.min(...xs);
        const h = Math.max(...ys) - Math.min(...ys);

        // bounding box roughly square
        const aspect = w / h;
        if (aspect < 0.5 || aspect > 2.0) return false;

        // path length check
        let pathLen = 0;
        for (let i = 1; i < points.length; i++) {
            const dx = points[i][0] - points[i-1][0];
            const dy = points[i][1] - points[i-1][1];
            pathLen += Math.sqrt(dx*dx + dy*dy);
        }
        if (pathLen < Math.max(w,h) * 1.5) return false;

        // average radius consistency
        const cx = (Math.max(...xs) + Math.min(...xs)) / 2;
        const cy = (Math.max(...ys) + Math.min(...ys)) / 2;
        const radii = points.map(p => Math.sqrt((p[0]-cx)**2 + (p[1]-cy)**2));
        const avgR = radii.reduce((a,b)=>a+b,0)/radii.length;
        const variance = radii.reduce((a,b)=>a+(b-avgR)**2,0)/radii.length;
        if (Math.sqrt(variance) > avgR * 0.4) return false;

        // start-end proximity
        const dx = points[0][0] - points[points.length-1][0];
        const dy = points[0][1] - points[points.length-1][1];
        const dist = Math.sqrt(dx*dx + dy*dy);
        if (dist > avgR * 0.6) return false;

        return true;
    }
})();