Enhanced Context Menu

Enhanced context menu with additional features and customization

  1. // ==UserScript==
  2. // @name Enhanced Context Menu
  3. // @author ALFHAZERO
  4. // @namespace http://
  5. // @version 2.1.1
  6. // @description Enhanced context menu with additional features and customization
  7. // @match *://*/*
  8. // @grant GM_setValue
  9. // @grant GM_getValue
  10. // @grant GM_deleteValue
  11. // ==/UserScript==
  12.  
  13. (function() {
  14. 'use strict';
  15.  
  16. /* 1. CONSTANTS & GLOBAL VARIABLES */
  17. const STORAGE_KEYS = {
  18. PREFERENCES: 'userPreferences',
  19. HIDDEN_ELEMENTS: 'hiddenElements',
  20. BOOKMARKS: 'globalBookmarks'
  21. };
  22.  
  23. /* 2. STYLES */
  24. const styles = {
  25. contextMenu: {
  26. position: 'fixed',
  27. backgroundColor: '#ffffff',
  28. border: '1px solid #cccccc',
  29. borderRadius: '4px',
  30. padding: '5px 0',
  31. minWidth: '200px',
  32. boxShadow: '0 2px 5px rgba(0,0,0,0.2)',
  33. zIndex: '9999999',
  34. fontSize: '14px',
  35. fontFamily: 'Arial, sans-serif'
  36. },
  37. menuItem: {
  38. padding: '8px 20px',
  39. cursor: 'pointer',
  40. listStyle: 'none',
  41. margin: '0',
  42. color: '#333333',
  43. transition: 'background-color 0.2s',
  44. display: 'flex',
  45. alignItems: 'center',
  46. gap: '8px'
  47. },
  48. divider: {
  49. height: '1px',
  50. backgroundColor: '#e0e0e0',
  51. margin: '5px 0'
  52. }
  53. };
  54.  
  55. /* 3. DEFAULT PREFERENCES */
  56. const defaultPreferences = {
  57. showBookmark: true,
  58. showShare: true,
  59. showOpen: true,
  60. showViewImage: true,
  61. showHideElement: true,
  62. showSaveImage: true,
  63. showReload: true,
  64. showPrint: true,
  65. showOpenInNewTab: true,
  66. showCopyOptions: true,
  67. menuPosition: 'right',
  68. darkMode: false,
  69. fontSize: 'normal',
  70. menuStyle: 'default',
  71. menuAnimation: true,
  72. menuTransparency: 1,
  73. timeoutDuration: 3000,
  74. vibrateOnLongPress: true,
  75. swipeGestures: true,
  76. longPressDelay: 500,
  77. preventDefaultContextMenu: true,
  78. touchMoveThreshold: 10
  79. };
  80.  
  81. /* 4. GLOBAL VARIABLES */
  82. let userPreferences = { ...defaultPreferences };
  83. let hiddenElements = [];
  84. let globalBookmarks = [];
  85.  
  86. /* 5. UTILITY FUNCTIONS */
  87. function applyStyles(element, styleObject) {
  88. Object.assign(element.style, styleObject);
  89. }
  90.  
  91. function removeContextMenu() {
  92. const existingMenu = document.querySelector('.context-menu');
  93. if (existingMenu) {
  94. existingMenu.remove();
  95. }
  96. }
  97.  
  98. function vibrate(duration = 50) {
  99. if (userPreferences.vibrateOnLongPress && navigator.vibrate) {
  100. navigator.vibrate(duration);
  101. }
  102. }
  103.  
  104. async function copyToClipboard(text) {
  105. try {
  106. await navigator.clipboard.writeText(text);
  107. showNotification('Berhasil disalin!');
  108. } catch (err) {
  109. // Fallback method for browsers that don't support clipboard API
  110. const textarea = document.createElement('textarea');
  111. textarea.value = text;
  112. document.body.appendChild(textarea);
  113. textarea.select();
  114. try {
  115. document.execCommand('copy');
  116. showNotification('Berhasil disalin!');
  117. } catch (e) {
  118. showNotification('Gagal menyalin teks');
  119. }
  120. document.body.removeChild(textarea);
  121. }
  122. }
  123.  
  124. function showNotification(message, duration = 2000) {
  125. const notification = document.createElement('div');
  126. applyStyles(notification, {
  127. position: 'fixed',
  128. bottom: '20px',
  129. left: '50%',
  130. transform: 'translateX(-50%)',
  131. backgroundColor: 'rgba(0, 0, 0, 0.8)',
  132. color: '#ffffff',
  133. padding: '10px 20px',
  134. borderRadius: '4px',
  135. zIndex: '10000',
  136. transition: 'opacity 0.3s'
  137. });
  138. notification.textContent = message;
  139. document.body.appendChild(notification);
  140.  
  141. setTimeout(() => {
  142. notification.style.opacity = '0';
  143. setTimeout(() => notification.remove(), 300);
  144. }, duration);
  145. }
  146.  
  147. function getSelectedText() {
  148. const selection = window.getSelection();
  149. return selection ? selection.toString().trim() : '';
  150. }
  151.  
  152. function getLinkUrl(element) {
  153. const linkElement = element.closest('a') || (element.tagName === 'A' ? element : null);
  154. return linkElement ? linkElement.href : null;
  155. }
  156.  
  157. function getImageUrl(element) {
  158. if (element.tagName === 'IMG') {
  159. return element.src;
  160. } else if (element.style.backgroundImage) {
  161. const match = element.style.backgroundImage.match(/url\(['"]?([^'"]+)['"]?\)/);
  162. return match ? match[1] : null;
  163. }
  164. return null;
  165. }
  166.  
  167. function getElementText(element) {
  168. // Get only the text directly contained by this element, excluding child elements
  169. let text = '';
  170. for (let node of element.childNodes) {
  171. if (node.nodeType === Node.TEXT_NODE) {
  172. text += node.textContent.trim();
  173. }
  174. }
  175. return text || element.innerText.trim() || '';
  176. }
  177.  
  178.  
  179. /* 6. MANAGER FUNCTIONS */
  180. function createBookmarkManager() {
  181. removeContextMenu();
  182. const manager = document.createElement('div');
  183. applyStyles(manager, {
  184. position: 'fixed',
  185. top: '50%',
  186. left: '50%',
  187. transform: 'translate(-50%, -50%)',
  188. backgroundColor: userPreferences.darkMode ? '#333' : '#fff',
  189. color: userPreferences.darkMode ? '#fff' : '#333',
  190. padding: '20px',
  191. borderRadius: '8px',
  192. boxShadow: '0 2px 10px rgba(0,0,0,0.2)',
  193. zIndex: '10000',
  194. maxWidth: '80%',
  195. maxHeight: '80vh',
  196. overflow: 'auto'
  197. });
  198.  
  199. const title = document.createElement('h3');
  200. title.textContent = 'Bookmark Manager';
  201. title.style.marginTop = '0';
  202. manager.appendChild(title);
  203.  
  204. const closeBtn = document.createElement('button');
  205. closeBtn.textContent = '×';
  206. applyStyles(closeBtn, {
  207. position: 'absolute',
  208. right: '10px',
  209. top: '10px',
  210. border: 'none',
  211. background: 'none',
  212. fontSize: '20px',
  213. cursor: 'pointer',
  214. color: userPreferences.darkMode ? '#fff' : '#333'
  215. });
  216. closeBtn.onclick = () => manager.remove();
  217. manager.appendChild(closeBtn);
  218.  
  219. if (globalBookmarks.length === 0) {
  220. const emptyMsg = document.createElement('p');
  221. emptyMsg.textContent = 'Tidak ada bookmark';
  222. manager.appendChild(emptyMsg);
  223. } else {
  224. const list = document.createElement('ul');
  225. applyStyles(list, {
  226. listStyle: 'none',
  227. padding: '0',
  228. margin: '0'
  229. });
  230.  
  231. globalBookmarks.forEach((bookmark, index) => {
  232. const item = document.createElement('li');
  233. applyStyles(item, {
  234. padding: '10px',
  235. borderBottom: '1px solid ' + (userPreferences.darkMode ? '#555' : '#eee'),
  236. display: 'flex',
  237. justifyContent: 'space-between',
  238. alignItems: 'center'
  239. });
  240.  
  241. const link = document.createElement('a');
  242. link.href = bookmark.url;
  243. link.textContent = bookmark.title;
  244. link.target = '_blank';
  245. applyStyles(link, {
  246. color: userPreferences.darkMode ? '#fff' : '#333',
  247. textDecoration: 'none'
  248. });
  249.  
  250. const deleteBtn = document.createElement('button');
  251. deleteBtn.textContent = 'Hapus';
  252. applyStyles(deleteBtn, {
  253. padding: '5px 10px',
  254. border: 'none',
  255. borderRadius: '4px',
  256. backgroundColor: '#ff4444',
  257. color: '#fff',
  258. cursor: 'pointer'
  259. });
  260. deleteBtn.onclick = () => {
  261. globalBookmarks.splice(index, 1);
  262. saveAllData();
  263. item.remove();
  264. if (globalBookmarks.length === 0) {
  265. manager.querySelector('ul').remove();
  266. const emptyMsg = document.createElement('p');
  267. emptyMsg.textContent = 'Tidak ada bookmark';
  268. manager.appendChild(emptyMsg);
  269. }
  270. };
  271.  
  272. item.appendChild(link);
  273. item.appendChild(deleteBtn);
  274. list.appendChild(item);
  275. });
  276.  
  277. manager.appendChild(list);
  278. }
  279.  
  280. document.body.appendChild(manager);
  281. }
  282.  
  283. function createHiddenElementsManager() {
  284. removeContextMenu();
  285. const manager = document.createElement('div');
  286. applyStyles(manager, {
  287. position: 'fixed',
  288. top: '50%',
  289. left: '50%',
  290. transform: 'translate(-50%, -50%)',
  291. backgroundColor: userPreferences.darkMode ? '#333' : '#fff',
  292. color: userPreferences.darkMode ? '#fff' : '#333',
  293. padding: '20px',
  294. borderRadius: '8px',
  295. boxShadow: '0 2px 10px rgba(0,0,0,0.2)',
  296. zIndex: '10000',
  297. maxWidth: '80%',
  298. maxHeight: '80vh',
  299. overflow: 'auto'
  300. });
  301.  
  302. const title = document.createElement('h3');
  303. title.textContent = 'Hidden Elements Manager';
  304. title.style.marginTop = '0';
  305. manager.appendChild(title);
  306.  
  307. const closeBtn = document.createElement('button');
  308. closeBtn.textContent = '×';
  309. applyStyles(closeBtn, {
  310. position: 'absolute',
  311. right: '10px',
  312. top: '10px',
  313. border: 'none',
  314. background: 'none',
  315. fontSize: '20px',
  316. cursor: 'pointer',
  317. color: userPreferences.darkMode ? '#fff' : '#333'
  318. });
  319. closeBtn.onclick = () => manager.remove();
  320. manager.appendChild(closeBtn);
  321.  
  322. if (hiddenElements.length === 0) {
  323. const emptyMsg = document.createElement('p');
  324. emptyMsg.textContent = 'Tidak ada elemen tersembunyi';
  325. manager.appendChild(emptyMsg);
  326. } else {
  327. const list = document.createElement('ul');
  328. applyStyles(list, {
  329. listStyle: 'none',
  330. padding: '0',
  331. margin: '0'
  332. });
  333.  
  334. hiddenElements.forEach((selector, index) => {
  335. const item = document.createElement('li');
  336. applyStyles(item, {
  337. padding: '10px',
  338. borderBottom: '1px solid ' + (userPreferences.darkMode ? '#555' : '#eee'),
  339. display: 'flex',
  340. justifyContent: 'space-between',
  341. alignItems: 'center'
  342. });
  343.  
  344. const text = document.createElement('span');
  345. text.textContent = selector;
  346.  
  347. const showBtn = document.createElement('button');
  348. showBtn.textContent = 'Tampilkan';
  349. applyStyles(showBtn, {
  350. padding: '5px 10px',
  351. border: 'none',
  352. borderRadius: '4px',
  353. backgroundColor: '#4CAF50',
  354. color: '#fff',
  355. cursor: 'pointer',
  356. marginRight: '5px'
  357. });
  358. showBtn.onclick = () => {
  359. try {
  360. const elements = document.querySelectorAll(selector);
  361. elements.forEach(el => {
  362. el.style.display = '';
  363. });
  364. hiddenElements.splice(index, 1);
  365. saveAllData();
  366. item.remove();
  367. if (hiddenElements.length === 0) {
  368. manager.querySelector('ul').remove();
  369. const emptyMsg = document.createElement('p');
  370. emptyMsg.textContent = 'Tidak ada elemen tersembunyi';
  371. manager.appendChild(emptyMsg);
  372. }
  373. } catch (e) {
  374. console.error('Error showing element:', e);
  375. }
  376. };
  377.  
  378. item.appendChild(text);
  379. item.appendChild(showBtn);
  380. list.appendChild(item);
  381. });
  382.  
  383. manager.appendChild(list);
  384. }
  385.  
  386. document.body.appendChild(manager);
  387. }
  388.  
  389.  
  390. /* 7. CONTEXT MENU CREATION */
  391. function createContextMenu(target, x, y) {
  392. removeContextMenu();
  393.  
  394. const menu = document.createElement('ul');
  395. menu.className = 'context-menu';
  396. applyStyles(menu, {
  397. ...styles.contextMenu,
  398. ...(userPreferences.darkMode ? {
  399. backgroundColor: '#333333',
  400. border: '1px solid #555555',
  401. color: '#ffffff'
  402. } : {}),
  403. opacity: userPreferences.menuTransparency
  404. });
  405.  
  406. // Menu Items Array
  407. const menuItems = [];
  408.  
  409. // Copy Options
  410. const selectedText = getSelectedText();
  411. if (selectedText) {
  412. menuItems.push({
  413. text: 'Salin Teks Terpilih',
  414. icon: '📋',
  415. action: () => copyToClipboard(selectedText)
  416. });
  417. }
  418.  
  419. // Get element text (excluding child elements)
  420. const elementText = getElementText(target);
  421. if (elementText && elementText !== selectedText) {
  422. menuItems.push({
  423. text: 'Salin Teks Elemen',
  424. icon: '📝',
  425. action: () => copyToClipboard(elementText)
  426. });
  427. }
  428.  
  429. // Image Options
  430. const imageUrl = getImageUrl(target);
  431. if (imageUrl) {
  432. if (userPreferences.showViewImage) {
  433. menuItems.push({
  434. text: 'Lihat Gambar',
  435. icon: '🖼️',
  436. action: () => window.open(imageUrl, '_blank')
  437. });
  438. }
  439. if (userPreferences.showSaveImage) {
  440. menuItems.push({
  441. text: 'Simpan Gambar',
  442. icon: '💾',
  443. action: () => {
  444. const link = document.createElement('a');
  445. link.href = imageUrl;
  446. link.download = imageUrl.split('/').pop() || 'image';
  447. link.click();
  448. }
  449. });
  450. }
  451. menuItems.push({
  452. text: 'Salin URL Gambar',
  453. icon: '🔗',
  454. action: () => copyToClipboard(imageUrl)
  455. });
  456. }
  457.  
  458. // Link Options
  459. const linkUrl = getLinkUrl(target);
  460. if (linkUrl) {
  461. menuItems.push({
  462. text: 'Salin Link',
  463. icon: '🔗',
  464. action: () => copyToClipboard(linkUrl)
  465. });
  466.  
  467. if (userPreferences.showOpenInNewTab) {
  468. menuItems.push({
  469. text: 'Buka di Tab Baru',
  470. icon: '📑',
  471. action: () => window.open(linkUrl, '_blank')
  472. });
  473. }
  474. }
  475.  
  476. // Standard Menu Items
  477. if (userPreferences.showBookmark) {
  478. menuItems.push({
  479. text: 'Bookmark',
  480. icon: '⭐',
  481. action: () => {
  482. const url = imageUrl || linkUrl || window.location.href;
  483. const title = elementText || document.title;
  484. globalBookmarks.push({ url, title });
  485. saveAllData();
  486. showNotification('Bookmark ditambahkan!');
  487. }
  488. });
  489. }
  490.  
  491. if (userPreferences.showShare) {
  492. menuItems.push({
  493. text: 'Bagikan',
  494. icon: '📤',
  495. action: () => {
  496. const url = imageUrl || linkUrl || window.location.href;
  497. if (navigator.share) {
  498. navigator.share({
  499. title: document.title,
  500. url: url
  501. }).catch(console.error);
  502. } else {
  503. copyToClipboard(url);
  504. }
  505. }
  506. });
  507. }
  508.  
  509. if (userPreferences.showHideElement) {
  510. menuItems.push({
  511. text: 'Sembunyikan Elemen',
  512. icon: '👁️',
  513. action: () => {
  514. target.style.display = 'none';
  515. const selector = target.tagName.toLowerCase() +
  516. (target.className ? '.' + target.className.replace(/\s+/g, '.') : '');
  517. hiddenElements.push(selector);
  518. saveAllData();
  519. showNotification('Elemen disembunyikan!');
  520. }
  521. });
  522. }
  523.  
  524. // Management Options
  525. menuItems.push({
  526. text: 'Kelola Bookmark',
  527. icon: '📚',
  528. action: () => createBookmarkManager()
  529. });
  530.  
  531. menuItems.push({
  532. text: 'Kelola Elemen Tersembunyi',
  533. icon: '👁️',
  534. action: () => createHiddenElementsManager()
  535. });
  536.  
  537. if (userPreferences.showReload) {
  538. menuItems.push({
  539. text: 'Muat Ulang',
  540. icon: '🔄',
  541. action: () => location.reload()
  542. });
  543. }
  544.  
  545. // Create Menu Items
  546. menuItems.forEach((item, index) => {
  547. if (index > 0) {
  548. const divider = document.createElement('li');
  549. applyStyles(divider, styles.divider);
  550. menu.appendChild(divider);
  551. }
  552.  
  553. const menuItem = document.createElement('li');
  554. applyStyles(menuItem, styles.menuItem);
  555. menuItem.innerHTML = `<span class="menu-icon">${item.icon}</span><span>${item.text}</span>`;
  556. if (userPreferences.darkMode) {
  557. menuItem.style.color = '#ffffff';
  558. }
  559.  
  560. menuItem.addEventListener('mouseover', () => {
  561. menuItem.style.backgroundColor = userPreferences.darkMode ? '#444444' : '#f0f0f0';
  562. });
  563.  
  564. menuItem.addEventListener('mouseout', () => {
  565. menuItem.style.backgroundColor = 'transparent';
  566. });
  567.  
  568. menuItem.addEventListener('click', (e) => {
  569. e.preventDefault();
  570. e.stopPropagation();
  571. item.action();
  572. removeContextMenu();
  573. });
  574.  
  575. menu.appendChild(menuItem);
  576. });
  577.  
  578.  
  579. // Position Menu
  580. const viewportWidth = window.innerWidth;
  581. const viewportHeight = window.innerHeight;
  582. const menuWidth = 200; // Estimated menu width
  583. const menuHeight = menuItems.length * 40; // Estimated menu height
  584.  
  585. let posX = x;
  586. let posY = y;
  587.  
  588. // Check horizontal position
  589. if (x + menuWidth > viewportWidth) {
  590. posX = viewportWidth - menuWidth - 10;
  591. }
  592.  
  593. // Check vertical position
  594. if (y + menuHeight > viewportHeight) {
  595. posY = viewportHeight - menuHeight - 10;
  596. }
  597.  
  598. applyStyles(menu, {
  599. left: posX + 'px',
  600. top: posY + 'px'
  601. });
  602.  
  603. document.body.appendChild(menu);
  604.  
  605. // Auto hide menu after timeout
  606. if (userPreferences.timeoutDuration > 0) {
  607. setTimeout(() => {
  608. if (document.body.contains(menu)) {
  609. menu.style.opacity = '0';
  610. setTimeout(() => removeContextMenu(), 300);
  611. }
  612. }, userPreferences.timeoutDuration);
  613. }
  614.  
  615. return menu;
  616. }
  617.  
  618. /* 8. LONG PRESS HANDLER */
  619. function setupLongPress(element) {
  620. if (element.hasAttribute('data-longpress-initialized')) return;
  621.  
  622. let timeoutId;
  623. let startX, startY;
  624. let isLongPress = false;
  625. const moveThreshold = userPreferences.touchMoveThreshold;
  626.  
  627. const startLongPress = (e) => {
  628. const coords = e.type.includes('touch') ?
  629. { x: e.touches[0].clientX, y: e.touches[0].clientY } :
  630. { x: e.clientX, y: e.clientY };
  631.  
  632. startX = coords.x;
  633. startY = coords.y;
  634. isLongPress = false;
  635.  
  636. timeoutId = setTimeout(() => {
  637. isLongPress = true;
  638. vibrate();
  639. createContextMenu(e.target, coords.x, coords.y);
  640. }, userPreferences.longPressDelay);
  641. };
  642.  
  643. const moveHandler = (e) => {
  644. if (!timeoutId) return;
  645.  
  646. const coords = e.type.includes('touch') ?
  647. { x: e.touches[0].clientX, y: e.touches[0].clientY } :
  648. { x: e.clientX, y: e.clientY };
  649.  
  650. if (Math.abs(coords.x - startX) > moveThreshold ||
  651. Math.abs(coords.y - startY) > moveThreshold) {
  652. clearTimeout(timeoutId);
  653. timeoutId = null;
  654. }
  655. };
  656.  
  657. const endLongPress = (e) => {
  658. clearTimeout(timeoutId);
  659. if (!isLongPress) return true;
  660. e.preventDefault();
  661. return false;
  662. };
  663.  
  664. // Touch Events
  665. element.addEventListener('touchstart', startLongPress, { passive: true });
  666. element.addEventListener('touchmove', moveHandler, { passive: true });
  667. element.addEventListener('touchend', endLongPress);
  668. element.addEventListener('touchcancel', endLongPress);
  669.  
  670. // Mouse Events
  671. element.addEventListener('mousedown', startLongPress);
  672. element.addEventListener('mousemove', moveHandler);
  673. element.addEventListener('mouseup', endLongPress);
  674. element.addEventListener('mouseleave', endLongPress);
  675.  
  676. // Context Menu Prevention
  677. if (userPreferences.preventDefaultContextMenu) {
  678. element.addEventListener('contextmenu', (e) => e.preventDefault());
  679. }
  680.  
  681. element.setAttribute('data-longpress-initialized', 'true');
  682. }
  683.  
  684. /* 9. INITIALIZATION AND SETUP */
  685. function setupGlobalEvents() {
  686. document.addEventListener('click', (e) => {
  687. const contextMenu = document.querySelector('.context-menu');
  688. if (contextMenu && !contextMenu.contains(e.target)) {
  689. removeContextMenu();
  690. }
  691. });
  692.  
  693. document.addEventListener('scroll', () => {
  694. removeContextMenu();
  695. });
  696.  
  697. document.addEventListener('keydown', (e) => {
  698. if (e.key === 'Escape') {
  699. removeContextMenu();
  700. }
  701. });
  702. }
  703.  
  704. /* 10. STORAGE FUNCTIONS */
  705. function loadAllData() {
  706. try {
  707. const savedPreferences = GM_getValue(STORAGE_KEYS.PREFERENCES);
  708. if (savedPreferences) {
  709. userPreferences = { ...defaultPreferences, ...JSON.parse(savedPreferences) };
  710. }
  711. const savedHiddenElements = GM_getValue(STORAGE_KEYS.HIDDEN_ELEMENTS);
  712. if (savedHiddenElements) {
  713. hiddenElements = JSON.parse(savedHiddenElements);
  714. }
  715. const savedBookmarks = GM_getValue(STORAGE_KEYS.BOOKMARKS);
  716. if (savedBookmarks) {
  717. globalBookmarks = JSON.parse(savedBookmarks);
  718. }
  719. } catch (e) {
  720. console.error('Error loading data:', e);
  721. }
  722. }
  723.  
  724. function saveAllData() {
  725. try {
  726. GM_setValue(STORAGE_KEYS.PREFERENCES, JSON.stringify(userPreferences));
  727. GM_setValue(STORAGE_KEYS.HIDDEN_ELEMENTS, JSON.stringify(hiddenElements));
  728. GM_setValue(STORAGE_KEYS.BOOKMARKS, JSON.stringify(globalBookmarks));
  729. } catch (e) {
  730. console.error('Error saving data:', e);
  731. }
  732. }
  733.  
  734. /* 11. INITIALIZATION */
  735. function init() {
  736. loadAllData();
  737. setupGlobalEvents();
  738. const supportedElements = [
  739. 'a', 'img', 'video',
  740. '[style*="background-image"]',
  741. '.product__sidebar__view__item',
  742. 'div', 'span', 'p',
  743. 'article', 'section',
  744. '.clickable',
  745. '[role="button"]',
  746. 'button',
  747. 'input[type="button"]',
  748. 'input[type="submit"]',
  749. 'iframe'
  750. ];
  751. supportedElements.forEach(selector => {
  752. document.querySelectorAll(selector).forEach(el => {
  753. if (!el.hasAttribute('data-longpress-initialized')) {
  754. setupLongPress(el);
  755. }
  756. });
  757. });
  758.  
  759. // Apply hidden elements
  760. hiddenElements.forEach(selector => {
  761. try {
  762. document.querySelectorAll(selector).forEach(el => {
  763. el.style.display = 'none';
  764. });
  765. } catch (e) {
  766. console.error('Error applying hidden element:', e);
  767. }
  768. });
  769.  
  770. // Auto save data every 5 minutes
  771. setInterval(saveAllData, 300000);
  772. }
  773.  
  774. // Start initialization
  775. if (document.readyState === 'loading') {
  776. document.addEventListener('DOMContentLoaded', init);
  777. } else {
  778. init();
  779. }
  780.  
  781. // Cleanup on page unload
  782. window.addEventListener('unload', saveAllData);
  783.  
  784. })();