// ==UserScript==
// @name Imgilism - Image Uploader (Improved)
// @namespace http://tampermonkey.net/
// @version 1.1.0
// @description Right-click image upload to Mobilism - preserves native context menu
// @author Sage & AI Improvements
// @match http://*/*
// @match https://*/*
// @grant GM_xmlhttpRequest
// @grant GM_registerMenuCommand
// @grant GM_notification
// @grant GM_setClipboard
// @run-at document-start
// ==/UserScript==
(function() {
'use strict';
let imgilismMenu = null;
let currentImageUrl = null;
let menuTimeout = null;
// Create styles for our menu addon
const style = document.createElement('style');
style.textContent = `
.imgilism-addon-menu {
position: fixed;
background: #2c3e50;
color: white;
border: 2px solid #3498db;
border-radius: 6px;
box-shadow: 0 4px 15px rgba(0,0,0,0.3);
z-index: 999999;
min-width: 180px;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Arial, sans-serif;
font-size: 13px;
opacity: 0;
transform: scale(0.95);
transition: all 0.15s ease-out;
}
.imgilism-addon-menu.show {
opacity: 1;
transform: scale(1);
}
.imgilism-menu-header {
padding: 8px 12px;
background: #3498db;
font-weight: bold;
border-radius: 4px 4px 0 0;
font-size: 12px;
text-align: center;
}
.imgilism-menu-item {
padding: 10px 15px;
cursor: pointer;
border-bottom: 1px solid #34495e;
transition: all 0.2s;
display: flex;
align-items: center;
}
.imgilism-menu-item:hover {
background: #3498db;
padding-left: 20px;
}
.imgilism-menu-item:last-child {
border-bottom: none;
border-radius: 0 0 4px 4px;
}
.imgilism-menu-item .size-info {
font-size: 11px;
opacity: 0.7;
margin-left: auto;
}
.imgilism-progress {
padding: 12px 15px;
background: #27ae60;
color: white;
font-style: italic;
text-align: center;
border-radius: 4px;
}
.imgilism-error {
padding: 12px 15px;
background: #e74c3c;
color: white;
text-align: center;
border-radius: 4px;
}
.imgilism-close-btn {
position: absolute;
top: 2px;
right: 5px;
background: none;
border: none;
color: white;
cursor: pointer;
font-size: 16px;
width: 20px;
height: 20px;
display: flex;
align-items: center;
justify-content: center;
}
`;
document.head.appendChild(style);
// Create our addon menu
function createAddonMenu() {
imgilismMenu = document.createElement('div');
imgilismMenu.className = 'imgilism-addon-menu';
imgilismMenu.innerHTML = `
<button class="imgilism-close-btn" title="Close">×</button>
<div class="imgilism-menu-header">📸 Imgilism Upload</div>
<div class="imgilism-menu-item" data-action="single">
Single <span class="size-info">320x240</span>
</div>
<div class="imgilism-menu-item" data-action="multiple">
Multiple <span class="size-info">240x160</span>
</div>
<div class="imgilism-menu-item" data-action="large">
Large <span class="size-info">500xAuto</span>
</div>
`;
document.body.appendChild(imgilismMenu);
// Add click handlers
imgilismMenu.addEventListener('click', function(e) {
if (e.target.classList.contains('imgilism-close-btn')) {
hideAddonMenu();
return;
}
const action = e.target.closest('.imgilism-menu-item')?.dataset.action;
if (action && currentImageUrl) {
uploadImage(currentImageUrl, action);
}
});
}
// Show addon menu near the mouse cursor
function showAddonMenu(x, y, imageUrl) {
currentImageUrl = imageUrl;
if (!imgilismMenu) {
createAddonMenu();
}
// Position menu, ensuring it stays on screen
const menuWidth = 180;
const menuHeight = 140;
let left = x + 10; // Offset from cursor
let top = y - 10;
// Keep menu on screen
if (left + menuWidth > window.innerWidth) {
left = x - menuWidth - 10;
}
if (top + menuHeight > window.innerHeight) {
top = y - menuHeight + 10;
}
imgilismMenu.style.left = left + 'px';
imgilismMenu.style.top = top + 'px';
imgilismMenu.style.display = 'block';
// Smooth show animation
setTimeout(() => imgilismMenu.classList.add('show'), 10);
// Auto-hide after 10 seconds if not used
clearTimeout(menuTimeout);
menuTimeout = setTimeout(hideAddonMenu, 10000);
}
// Hide addon menu
function hideAddonMenu() {
if (imgilismMenu) {
imgilismMenu.classList.remove('show');
setTimeout(() => {
if (imgilismMenu) imgilismMenu.style.display = 'none';
}, 150);
}
currentImageUrl = null;
clearTimeout(menuTimeout);
}
// Listen for right-clicks on images
document.addEventListener('contextmenu', function(e) {
const img = e.target.closest('img');
if (img && img.src) {
// Small delay to let native menu appear first
setTimeout(() => {
showAddonMenu(e.clientX, e.clientY, img.src);
}, 100);
} else {
hideAddonMenu();
}
});
// Hide menu when clicking elsewhere or scrolling
document.addEventListener('click', hideAddonMenu);
document.addEventListener('scroll', hideAddonMenu);
// Upload image function (keeping original logic)
function uploadImage(imageUrl, sizeOption) {
console.log('Starting image upload for:', imageUrl, 'with size option:', sizeOption);
// Show progress
if (imgilismMenu) {
imgilismMenu.innerHTML = '<div class="imgilism-progress">🔄 Uploading image...</div>';
}
// Configure size options
let height, width, responseId;
switch(sizeOption) {
case 'single':
height = '320';
width = '';
responseId = 'codehtml';
break;
case 'multiple':
height = '240';
width = '160';
responseId = 'codelbb';
break;
case 'large':
height = '500';
width = '';
responseId = 'codehtml';
break;
}
// Fetch the image
GM_xmlhttpRequest({
method: 'GET',
url: imageUrl,
responseType: 'blob',
timeout: 30000,
onload: function(response) {
if (response.status === 200) {
const blob = response.response;
const fileName = imageUrl.split(/[?#]/)[0].split('/').pop() || 'image.jpg';
const file = new File([blob], fileName, { type: blob.type });
// Create FormData
const formData = new FormData();
formData.append('links', '');
formData.append('imgUrl', '');
formData.append('fileName[]', fileName);
formData.append('Search files', 'Browse');
formData.append('file[]', file);
formData.append('alt[]', fileName.replace(/[-_]/g, ' ').replace(/\.[^.]*$/, ''));
formData.append('private[0]', '1');
formData.append('new_height[]', height);
formData.append('new_width[]', width);
formData.append('submit', 'Upload');
// Upload to Mobilism
GM_xmlhttpRequest({
method: 'POST',
url: 'https://images.mobilism.org/upload.php',
data: formData,
timeout: 60000,
onload: function(uploadResponse) {
if (uploadResponse.status === 200) {
try {
const parser = new DOMParser();
const responseDoc = parser.parseFromString(uploadResponse.responseText, 'text/html');
const urlElement = responseDoc.getElementById(responseId);
if (urlElement && urlElement.value) {
const uploadedUrl = urlElement.value.trim();
GM_setClipboard(uploadedUrl);
// Show success
if (imgilismMenu) {
imgilismMenu.innerHTML = `<div class="imgilism-progress">✅ Copied to clipboard!<br><small>${uploadedUrl}</small></div>`;
setTimeout(hideAddonMenu, 3000);
}
GM_notification({
text: 'Image uploaded! URL copied to clipboard.',
title: 'Imgilism Success'
});
} else {
throw new Error('Upload URL not found in response');
}
} catch (e) {
console.error('Upload processing error:', e);
showError('Failed to process upload response');
}
} else {
showError('Upload failed: ' + uploadResponse.statusText);
}
},
onerror: function() {
showError('Upload request failed');
},
ontimeout: function() {
showError('Upload timed out');
}
});
} else {
showError('Failed to fetch image: ' + response.statusText);
}
},
onerror: function() {
showError('Failed to fetch image');
},
ontimeout: function() {
showError('Image fetch timed out');
}
});
}
function showError(message) {
console.error('Imgilism error:', message);
if (imgilismMenu) {
imgilismMenu.innerHTML = `<div class="imgilism-error">❌ ${message}</div>`;
setTimeout(hideAddonMenu, 4000);
}
GM_notification({
text: message,
title: 'Imgilism Error'
});
}
})();