// ==UserScript==
// @name Enhanced Context Menu
// @author ALFHAZERO
// @namespace http://
// @version 2.1.1
// @description Enhanced context menu with additional features and customization
// @match *://*/*
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_deleteValue
// ==/UserScript==
(function() {
'use strict';
/* 1. CONSTANTS & GLOBAL VARIABLES */
const STORAGE_KEYS = {
PREFERENCES: 'userPreferences',
HIDDEN_ELEMENTS: 'hiddenElements',
BOOKMARKS: 'globalBookmarks'
};
/* 2. STYLES */
const styles = {
contextMenu: {
position: 'fixed',
backgroundColor: '#ffffff',
border: '1px solid #cccccc',
borderRadius: '4px',
padding: '5px 0',
minWidth: '200px',
boxShadow: '0 2px 5px rgba(0,0,0,0.2)',
zIndex: '9999999',
fontSize: '14px',
fontFamily: 'Arial, sans-serif'
},
menuItem: {
padding: '8px 20px',
cursor: 'pointer',
listStyle: 'none',
margin: '0',
color: '#333333',
transition: 'background-color 0.2s',
display: 'flex',
alignItems: 'center',
gap: '8px'
},
divider: {
height: '1px',
backgroundColor: '#e0e0e0',
margin: '5px 0'
}
};
/* 3. DEFAULT PREFERENCES */
const defaultPreferences = {
showBookmark: true,
showShare: true,
showOpen: true,
showViewImage: true,
showHideElement: true,
showSaveImage: true,
showReload: true,
showPrint: true,
showOpenInNewTab: true,
showCopyOptions: true,
menuPosition: 'right',
darkMode: false,
fontSize: 'normal',
menuStyle: 'default',
menuAnimation: true,
menuTransparency: 1,
timeoutDuration: 3000,
vibrateOnLongPress: true,
swipeGestures: true,
longPressDelay: 500,
preventDefaultContextMenu: true,
touchMoveThreshold: 10
};
/* 4. GLOBAL VARIABLES */
let userPreferences = { ...defaultPreferences };
let hiddenElements = [];
let globalBookmarks = [];
/* 5. UTILITY FUNCTIONS */
function applyStyles(element, styleObject) {
Object.assign(element.style, styleObject);
}
function removeContextMenu() {
const existingMenu = document.querySelector('.context-menu');
if (existingMenu) {
existingMenu.remove();
}
}
function vibrate(duration = 50) {
if (userPreferences.vibrateOnLongPress && navigator.vibrate) {
navigator.vibrate(duration);
}
}
async function copyToClipboard(text) {
try {
await navigator.clipboard.writeText(text);
showNotification('Berhasil disalin!');
} catch (err) {
// Fallback method for browsers that don't support clipboard API
const textarea = document.createElement('textarea');
textarea.value = text;
document.body.appendChild(textarea);
textarea.select();
try {
document.execCommand('copy');
showNotification('Berhasil disalin!');
} catch (e) {
showNotification('Gagal menyalin teks');
}
document.body.removeChild(textarea);
}
}
function showNotification(message, duration = 2000) {
const notification = document.createElement('div');
applyStyles(notification, {
position: 'fixed',
bottom: '20px',
left: '50%',
transform: 'translateX(-50%)',
backgroundColor: 'rgba(0, 0, 0, 0.8)',
color: '#ffffff',
padding: '10px 20px',
borderRadius: '4px',
zIndex: '10000',
transition: 'opacity 0.3s'
});
notification.textContent = message;
document.body.appendChild(notification);
setTimeout(() => {
notification.style.opacity = '0';
setTimeout(() => notification.remove(), 300);
}, duration);
}
function getSelectedText() {
const selection = window.getSelection();
return selection ? selection.toString().trim() : '';
}
function getLinkUrl(element) {
const linkElement = element.closest('a') || (element.tagName === 'A' ? element : null);
return linkElement ? linkElement.href : null;
}
function getImageUrl(element) {
if (element.tagName === 'IMG') {
return element.src;
} else if (element.style.backgroundImage) {
const match = element.style.backgroundImage.match(/url\(['"]?([^'"]+)['"]?\)/);
return match ? match[1] : null;
}
return null;
}
function getElementText(element) {
// Get only the text directly contained by this element, excluding child elements
let text = '';
for (let node of element.childNodes) {
if (node.nodeType === Node.TEXT_NODE) {
text += node.textContent.trim();
}
}
return text || element.innerText.trim() || '';
}
/* 6. MANAGER FUNCTIONS */
function createBookmarkManager() {
removeContextMenu();
const manager = document.createElement('div');
applyStyles(manager, {
position: 'fixed',
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)',
backgroundColor: userPreferences.darkMode ? '#333' : '#fff',
color: userPreferences.darkMode ? '#fff' : '#333',
padding: '20px',
borderRadius: '8px',
boxShadow: '0 2px 10px rgba(0,0,0,0.2)',
zIndex: '10000',
maxWidth: '80%',
maxHeight: '80vh',
overflow: 'auto'
});
const title = document.createElement('h3');
title.textContent = 'Bookmark Manager';
title.style.marginTop = '0';
manager.appendChild(title);
const closeBtn = document.createElement('button');
closeBtn.textContent = '×';
applyStyles(closeBtn, {
position: 'absolute',
right: '10px',
top: '10px',
border: 'none',
background: 'none',
fontSize: '20px',
cursor: 'pointer',
color: userPreferences.darkMode ? '#fff' : '#333'
});
closeBtn.onclick = () => manager.remove();
manager.appendChild(closeBtn);
if (globalBookmarks.length === 0) {
const emptyMsg = document.createElement('p');
emptyMsg.textContent = 'Tidak ada bookmark';
manager.appendChild(emptyMsg);
} else {
const list = document.createElement('ul');
applyStyles(list, {
listStyle: 'none',
padding: '0',
margin: '0'
});
globalBookmarks.forEach((bookmark, index) => {
const item = document.createElement('li');
applyStyles(item, {
padding: '10px',
borderBottom: '1px solid ' + (userPreferences.darkMode ? '#555' : '#eee'),
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center'
});
const link = document.createElement('a');
link.href = bookmark.url;
link.textContent = bookmark.title;
link.target = '_blank';
applyStyles(link, {
color: userPreferences.darkMode ? '#fff' : '#333',
textDecoration: 'none'
});
const deleteBtn = document.createElement('button');
deleteBtn.textContent = 'Hapus';
applyStyles(deleteBtn, {
padding: '5px 10px',
border: 'none',
borderRadius: '4px',
backgroundColor: '#ff4444',
color: '#fff',
cursor: 'pointer'
});
deleteBtn.onclick = () => {
globalBookmarks.splice(index, 1);
saveAllData();
item.remove();
if (globalBookmarks.length === 0) {
manager.querySelector('ul').remove();
const emptyMsg = document.createElement('p');
emptyMsg.textContent = 'Tidak ada bookmark';
manager.appendChild(emptyMsg);
}
};
item.appendChild(link);
item.appendChild(deleteBtn);
list.appendChild(item);
});
manager.appendChild(list);
}
document.body.appendChild(manager);
}
function createHiddenElementsManager() {
removeContextMenu();
const manager = document.createElement('div');
applyStyles(manager, {
position: 'fixed',
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)',
backgroundColor: userPreferences.darkMode ? '#333' : '#fff',
color: userPreferences.darkMode ? '#fff' : '#333',
padding: '20px',
borderRadius: '8px',
boxShadow: '0 2px 10px rgba(0,0,0,0.2)',
zIndex: '10000',
maxWidth: '80%',
maxHeight: '80vh',
overflow: 'auto'
});
const title = document.createElement('h3');
title.textContent = 'Hidden Elements Manager';
title.style.marginTop = '0';
manager.appendChild(title);
const closeBtn = document.createElement('button');
closeBtn.textContent = '×';
applyStyles(closeBtn, {
position: 'absolute',
right: '10px',
top: '10px',
border: 'none',
background: 'none',
fontSize: '20px',
cursor: 'pointer',
color: userPreferences.darkMode ? '#fff' : '#333'
});
closeBtn.onclick = () => manager.remove();
manager.appendChild(closeBtn);
if (hiddenElements.length === 0) {
const emptyMsg = document.createElement('p');
emptyMsg.textContent = 'Tidak ada elemen tersembunyi';
manager.appendChild(emptyMsg);
} else {
const list = document.createElement('ul');
applyStyles(list, {
listStyle: 'none',
padding: '0',
margin: '0'
});
hiddenElements.forEach((selector, index) => {
const item = document.createElement('li');
applyStyles(item, {
padding: '10px',
borderBottom: '1px solid ' + (userPreferences.darkMode ? '#555' : '#eee'),
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center'
});
const text = document.createElement('span');
text.textContent = selector;
const showBtn = document.createElement('button');
showBtn.textContent = 'Tampilkan';
applyStyles(showBtn, {
padding: '5px 10px',
border: 'none',
borderRadius: '4px',
backgroundColor: '#4CAF50',
color: '#fff',
cursor: 'pointer',
marginRight: '5px'
});
showBtn.onclick = () => {
try {
const elements = document.querySelectorAll(selector);
elements.forEach(el => {
el.style.display = '';
});
hiddenElements.splice(index, 1);
saveAllData();
item.remove();
if (hiddenElements.length === 0) {
manager.querySelector('ul').remove();
const emptyMsg = document.createElement('p');
emptyMsg.textContent = 'Tidak ada elemen tersembunyi';
manager.appendChild(emptyMsg);
}
} catch (e) {
console.error('Error showing element:', e);
}
};
item.appendChild(text);
item.appendChild(showBtn);
list.appendChild(item);
});
manager.appendChild(list);
}
document.body.appendChild(manager);
}
/* 7. CONTEXT MENU CREATION */
function createContextMenu(target, x, y) {
removeContextMenu();
const menu = document.createElement('ul');
menu.className = 'context-menu';
applyStyles(menu, {
...styles.contextMenu,
...(userPreferences.darkMode ? {
backgroundColor: '#333333',
border: '1px solid #555555',
color: '#ffffff'
} : {}),
opacity: userPreferences.menuTransparency
});
// Menu Items Array
const menuItems = [];
// Copy Options
const selectedText = getSelectedText();
if (selectedText) {
menuItems.push({
text: 'Salin Teks Terpilih',
icon: '📋',
action: () => copyToClipboard(selectedText)
});
}
// Get element text (excluding child elements)
const elementText = getElementText(target);
if (elementText && elementText !== selectedText) {
menuItems.push({
text: 'Salin Teks Elemen',
icon: '📝',
action: () => copyToClipboard(elementText)
});
}
// Image Options
const imageUrl = getImageUrl(target);
if (imageUrl) {
if (userPreferences.showViewImage) {
menuItems.push({
text: 'Lihat Gambar',
icon: '🖼️',
action: () => window.open(imageUrl, '_blank')
});
}
if (userPreferences.showSaveImage) {
menuItems.push({
text: 'Simpan Gambar',
icon: '💾',
action: () => {
const link = document.createElement('a');
link.href = imageUrl;
link.download = imageUrl.split('/').pop() || 'image';
link.click();
}
});
}
menuItems.push({
text: 'Salin URL Gambar',
icon: '🔗',
action: () => copyToClipboard(imageUrl)
});
}
// Link Options
const linkUrl = getLinkUrl(target);
if (linkUrl) {
menuItems.push({
text: 'Salin Link',
icon: '🔗',
action: () => copyToClipboard(linkUrl)
});
if (userPreferences.showOpenInNewTab) {
menuItems.push({
text: 'Buka di Tab Baru',
icon: '📑',
action: () => window.open(linkUrl, '_blank')
});
}
}
// Standard Menu Items
if (userPreferences.showBookmark) {
menuItems.push({
text: 'Bookmark',
icon: '⭐',
action: () => {
const url = imageUrl || linkUrl || window.location.href;
const title = elementText || document.title;
globalBookmarks.push({ url, title });
saveAllData();
showNotification('Bookmark ditambahkan!');
}
});
}
if (userPreferences.showShare) {
menuItems.push({
text: 'Bagikan',
icon: '📤',
action: () => {
const url = imageUrl || linkUrl || window.location.href;
if (navigator.share) {
navigator.share({
title: document.title,
url: url
}).catch(console.error);
} else {
copyToClipboard(url);
}
}
});
}
if (userPreferences.showHideElement) {
menuItems.push({
text: 'Sembunyikan Elemen',
icon: '👁️',
action: () => {
target.style.display = 'none';
const selector = target.tagName.toLowerCase() +
(target.className ? '.' + target.className.replace(/\s+/g, '.') : '');
hiddenElements.push(selector);
saveAllData();
showNotification('Elemen disembunyikan!');
}
});
}
// Management Options
menuItems.push({
text: 'Kelola Bookmark',
icon: '📚',
action: () => createBookmarkManager()
});
menuItems.push({
text: 'Kelola Elemen Tersembunyi',
icon: '👁️',
action: () => createHiddenElementsManager()
});
if (userPreferences.showReload) {
menuItems.push({
text: 'Muat Ulang',
icon: '🔄',
action: () => location.reload()
});
}
// Create Menu Items
menuItems.forEach((item, index) => {
if (index > 0) {
const divider = document.createElement('li');
applyStyles(divider, styles.divider);
menu.appendChild(divider);
}
const menuItem = document.createElement('li');
applyStyles(menuItem, styles.menuItem);
menuItem.innerHTML = `<span class="menu-icon">${item.icon}</span><span>${item.text}</span>`;
if (userPreferences.darkMode) {
menuItem.style.color = '#ffffff';
}
menuItem.addEventListener('mouseover', () => {
menuItem.style.backgroundColor = userPreferences.darkMode ? '#444444' : '#f0f0f0';
});
menuItem.addEventListener('mouseout', () => {
menuItem.style.backgroundColor = 'transparent';
});
menuItem.addEventListener('click', (e) => {
e.preventDefault();
e.stopPropagation();
item.action();
removeContextMenu();
});
menu.appendChild(menuItem);
});
// Position Menu
const viewportWidth = window.innerWidth;
const viewportHeight = window.innerHeight;
const menuWidth = 200; // Estimated menu width
const menuHeight = menuItems.length * 40; // Estimated menu height
let posX = x;
let posY = y;
// Check horizontal position
if (x + menuWidth > viewportWidth) {
posX = viewportWidth - menuWidth - 10;
}
// Check vertical position
if (y + menuHeight > viewportHeight) {
posY = viewportHeight - menuHeight - 10;
}
applyStyles(menu, {
left: posX + 'px',
top: posY + 'px'
});
document.body.appendChild(menu);
// Auto hide menu after timeout
if (userPreferences.timeoutDuration > 0) {
setTimeout(() => {
if (document.body.contains(menu)) {
menu.style.opacity = '0';
setTimeout(() => removeContextMenu(), 300);
}
}, userPreferences.timeoutDuration);
}
return menu;
}
/* 8. LONG PRESS HANDLER */
function setupLongPress(element) {
if (element.hasAttribute('data-longpress-initialized')) return;
let timeoutId;
let startX, startY;
let isLongPress = false;
const moveThreshold = userPreferences.touchMoveThreshold;
const startLongPress = (e) => {
const coords = e.type.includes('touch') ?
{ x: e.touches[0].clientX, y: e.touches[0].clientY } :
{ x: e.clientX, y: e.clientY };
startX = coords.x;
startY = coords.y;
isLongPress = false;
timeoutId = setTimeout(() => {
isLongPress = true;
vibrate();
createContextMenu(e.target, coords.x, coords.y);
}, userPreferences.longPressDelay);
};
const moveHandler = (e) => {
if (!timeoutId) return;
const coords = e.type.includes('touch') ?
{ x: e.touches[0].clientX, y: e.touches[0].clientY } :
{ x: e.clientX, y: e.clientY };
if (Math.abs(coords.x - startX) > moveThreshold ||
Math.abs(coords.y - startY) > moveThreshold) {
clearTimeout(timeoutId);
timeoutId = null;
}
};
const endLongPress = (e) => {
clearTimeout(timeoutId);
if (!isLongPress) return true;
e.preventDefault();
return false;
};
// Touch Events
element.addEventListener('touchstart', startLongPress, { passive: true });
element.addEventListener('touchmove', moveHandler, { passive: true });
element.addEventListener('touchend', endLongPress);
element.addEventListener('touchcancel', endLongPress);
// Mouse Events
element.addEventListener('mousedown', startLongPress);
element.addEventListener('mousemove', moveHandler);
element.addEventListener('mouseup', endLongPress);
element.addEventListener('mouseleave', endLongPress);
// Context Menu Prevention
if (userPreferences.preventDefaultContextMenu) {
element.addEventListener('contextmenu', (e) => e.preventDefault());
}
element.setAttribute('data-longpress-initialized', 'true');
}
/* 9. INITIALIZATION AND SETUP */
function setupGlobalEvents() {
document.addEventListener('click', (e) => {
const contextMenu = document.querySelector('.context-menu');
if (contextMenu && !contextMenu.contains(e.target)) {
removeContextMenu();
}
});
document.addEventListener('scroll', () => {
removeContextMenu();
});
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape') {
removeContextMenu();
}
});
}
/* 10. STORAGE FUNCTIONS */
function loadAllData() {
try {
const savedPreferences = GM_getValue(STORAGE_KEYS.PREFERENCES);
if (savedPreferences) {
userPreferences = { ...defaultPreferences, ...JSON.parse(savedPreferences) };
}
const savedHiddenElements = GM_getValue(STORAGE_KEYS.HIDDEN_ELEMENTS);
if (savedHiddenElements) {
hiddenElements = JSON.parse(savedHiddenElements);
}
const savedBookmarks = GM_getValue(STORAGE_KEYS.BOOKMARKS);
if (savedBookmarks) {
globalBookmarks = JSON.parse(savedBookmarks);
}
} catch (e) {
console.error('Error loading data:', e);
}
}
function saveAllData() {
try {
GM_setValue(STORAGE_KEYS.PREFERENCES, JSON.stringify(userPreferences));
GM_setValue(STORAGE_KEYS.HIDDEN_ELEMENTS, JSON.stringify(hiddenElements));
GM_setValue(STORAGE_KEYS.BOOKMARKS, JSON.stringify(globalBookmarks));
} catch (e) {
console.error('Error saving data:', e);
}
}
/* 11. INITIALIZATION */
function init() {
loadAllData();
setupGlobalEvents();
const supportedElements = [
'a', 'img', 'video',
'[style*="background-image"]',
'.product__sidebar__view__item',
'div', 'span', 'p',
'article', 'section',
'.clickable',
'[role="button"]',
'button',
'input[type="button"]',
'input[type="submit"]',
'iframe'
];
supportedElements.forEach(selector => {
document.querySelectorAll(selector).forEach(el => {
if (!el.hasAttribute('data-longpress-initialized')) {
setupLongPress(el);
}
});
});
// Apply hidden elements
hiddenElements.forEach(selector => {
try {
document.querySelectorAll(selector).forEach(el => {
el.style.display = 'none';
});
} catch (e) {
console.error('Error applying hidden element:', e);
}
});
// Auto save data every 5 minutes
setInterval(saveAllData, 300000);
}
// Start initialization
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
// Cleanup on page unload
window.addEventListener('unload', saveAllData);
})();