Instagram Image Context Menu

Custom context menu untuk Open New Tab & Save Image As di IG

이 스크립트를 설치하려면 Tampermonkey, Greasemonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램을 설치해야 합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Userscripts와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 유저 스크립트 관리자 확장 프로그램이 필요합니다.

(이미 유저 스크립트 관리자가 설치되어 있습니다. 설치를 진행합니다!)

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

(이미 유저 스타일 관리자가 설치되어 있습니다. 설치를 진행합니다!)

// ==UserScript==
// @name         Instagram Image Context Menu
// @namespace    http://tampermonkey.net/
// @version      4.0
// @description  Custom context menu untuk Open New Tab & Save Image As di IG
// @author       XIREN
// @match        *://*.instagram.com/*
// @grant        GM_download
// @grant        GM_openInTab
// ==/UserScript==

(function() {
    'use strict';

    // 1. Setup UI untuk Custom Context Menu
    const menu = document.createElement('div');
    menu.id = 'ig-custom-context-menu';
    menu.style.position = 'fixed';
    menu.style.backgroundColor = '#262626'; // Warna dark mode IG
    menu.style.border = '1px solid #363636';
    menu.style.borderRadius = '8px';
    menu.style.padding = '5px 0';
    menu.style.zIndex = '9999999';
    menu.style.display = 'none';
    menu.style.boxShadow = '0 4px 12px rgba(0,0,0,0.5)';
    menu.style.fontFamily = '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif';

    const createOption = (text) => {
        const opt = document.createElement('div');
        opt.innerText = text;
        opt.style.padding = '10px 20px';
        opt.style.color = '#fff';
        opt.style.cursor = 'pointer';
        opt.style.fontSize = '14px';
        opt.style.fontWeight = '500';
        opt.onmouseover = () => opt.style.backgroundColor = '#363636';
        opt.onmouseout = () => opt.style.backgroundColor = 'transparent';
        return opt;
    };

    const openBtn = createOption('🔎 Fullscreen View');
    const saveBtn = createOption('💾 Unduh Gambar JPG');

    menu.appendChild(openBtn);
    menu.appendChild(saveBtn);
    document.body.appendChild(menu);

    let currentImgUrl = '';

    // 2. Event Listener buat nangkap Klik Kanan (Context Menu)
    document.addEventListener('contextmenu', function(e) {
        let target = e.target;
        let img = null;

        // Cek target yang diklik, cari elemen <img> di level atasnya (buat nembus overlay IG)
        if (target.tagName.toLowerCase() === 'img') {
            img = target;
        } else {
            let parent = target.parentElement;
            // Naik maksimal 4 level DOM buat nyari gambar yang ditamengin
            for (let i = 0; i < 4; i++) {
                if (!parent) break;
                img = parent.querySelector('img');
                if (img) break;
                parent = parent.parentElement;
            }
        }

        // Kalau ketemu gambar dan bukan sekadar icon/foto profil kecil
        if (img && img.src && !img.src.includes('profile_pic')) {
            e.preventDefault(); // Blokir context menu bawaan browser
            currentImgUrl = img.src;

            // Atur posisi menu pas di koordinat mouse
            menu.style.left = `${e.clientX}px`;
            menu.style.top = `${e.clientY}px`;
            menu.style.display = 'block';
        } else {
            menu.style.display = 'none'; // Sembunyiin kalau klik di luar gambar
        }
    });

    // 3. Ilangin menu kalau klik kiri di sembarang tempat
    document.addEventListener('click', function(e) {
        if (e.target !== openBtn && e.target !== saveBtn) {
            menu.style.display = 'none';
        }
    });

    // 4. Aksi: Open in New Tab
    openBtn.addEventListener('click', function() {
        if (currentImgUrl) {
            GM_openInTab(currentImgUrl, { active: true });
            menu.style.display = 'none';
        }
    });

    // 5. Aksi: Save Image As (Otomatis force download)
    saveBtn.addEventListener('click', function() {
        if (currentImgUrl) {
            // Ekstrak ID/Nama file dari URL asli IG
            let rawUrl = currentImgUrl.split('?')[0];
            let fileName = rawUrl.substring(rawUrl.lastIndexOf('/') + 1) || `IG_IMG_${Date.now()}.jpg`;

            // Panggil API Tampermonkey buat maksa buka dialog download
            GM_download({
                url: currentImgUrl,
                name: fileName,
                saveAs: true // Mancing munculnya dialog "Save As..."
            });
            menu.style.display = 'none';
        }
    });

})();