// ==UserScript==
// @name Mitmachim Top - נצפו לאחרונה (v2.0.4 - Syntax Check)
// @namespace http://tampermonkey.net/
// @version 2.0.4
// @description בדיקה מחודשת של תחביר ותפיסת שגיאות מוקדמות.
// @author לאצי&AI
// @match https://mitmachim.top/*
// @icon https://mitmachim.top/assets/uploads/files/1744298283200-font_awesome_5_solid_history.svg.png
// @grant GM_addStyle
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_deleteValue
// @run-at document-idle
// ==/UserScript==
(function() {
'use strict';
function ensureDependencies(callback) {
let checks = 0;
const MAX_CHECKS = 120;
const CHECK_INTERVAL = 100;
console.log("[LastViewed] Starting dependency check...");
const interval = setInterval(function() {
const jQueryReady = typeof $ === 'function'; // More robust check for jQuery
const $navList = jQueryReady ? $('nav[component="sidebar/left"] ul#main-nav') : null;
const navListReady = $navList && $navList.length > 0;
if (navListReady) {
clearInterval(interval);
console.log(`[LastViewed] Dependencies ($) AND Nav List found after ${checks + 1} checks.`);
try {
callback(); // Run the main script logic
} catch(e) {
console.error("[LastViewed] Error executing initialize callback:", e);
}
} else {
checks++;
if (checks >= MAX_CHECKS) {
clearInterval(interval);
console.error(`[LastViewed] Dependency check FAILED after ${MAX_CHECKS} checks (Timeout).`);
// Provide detailed status on failure
console.error(`[LastViewed] Final Status: jQuery=${typeof $ === 'function'}, NavList Element Found=${jQueryReady ? $('nav[component="sidebar/left"] ul#main-nav').length : 'N/A (jQuery missing)'}`);
}
}
}, CHECK_INTERVAL);
}
const STORAGE_KEY = 'mitmachim_last_viewed_topics';
const MAX_HISTORY_ITEMS = 20;
const TOPIC_URL_PREFIX = 'https://mitmachim.top/topic/';
const DEBOUNCE_DELAY = 300;
const styles = `
/* Popup Styles */
#last-viewed-popup { display: flex; flex-direction: column; position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 90%; max-width: 550px; max-height: 80vh; overflow-y: auto; background-color: var(--bs-body-bg, #ffffff); color: var(--bs-body-color, #212529); border: 1px solid var(--bs-border-color, #dee2e6); box-shadow: 0 5px 15px rgba(0,0,0,0.2); padding: 20px; z-index: 1060; direction: rtl; font-family: "Assistant", sans-serif; font-size: 14px; border-radius: var(--bs-border-radius, 0.375rem); }
#last-viewed-popup h2 { flex-shrink: 0; margin-top: 0; margin-bottom: 10px; padding-bottom: 10px; border-bottom: 1px solid var(--bs-border-color-translucent, rgba(0,0,0,0.175)); color: var(--bs-heading-color, inherit); text-align: right; font-size: 1.25rem; font-weight: 600; }
#last-viewed-popup ul#last-viewed-list { flex-grow: 1; list-style: none; padding: 0; margin: 0; overflow-y: auto; }
#last-viewed-popup li { margin-bottom: 10px; padding-bottom: 10px; border-bottom: 1px dashed var(--bs-border-color-translucent, rgba(0,0,0,0.1)); text-align: right; line-height: 1.5; }
#last-viewed-popup li:last-child { border-bottom: none; margin-bottom: 0; padding-bottom: 0; }
#last-viewed-popup a { text-decoration: none; color: var(--bs-link-color, #0d6efd); font-weight: 600; }
#last-viewed-popup a:hover { text-decoration: underline; color: var(--bs-link-hover-color, #0a58ca); }
#last-viewed-popup .popup-controls { flex-shrink: 0; display: flex; justify-content: space-between; align-items: center; margin-top: 15px; padding-top: 10px; border-top: 1px solid var(--bs-border-color-translucent, rgba(0,0,0,0.175)); }
#last-viewed-popup .close-popup-btn, #last-viewed-popup #clear-last-viewed { padding: 0.375rem 0.75rem; font-size: 0.9rem; border-radius: var(--bs-border-radius-sm, 0.25rem); cursor: pointer; text-align: center; vertical-align: middle; user-select: none; border: 1px solid transparent; transition: color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out; }
#last-viewed-popup .close-popup-btn { color: var(--bs-secondary-color, #6c757d); background-color: var(--bs-secondary-bg, #e9ecef); border-color: var(--bs-secondary-border, #dee2e6); }
#last-viewed-popup .close-popup-btn:hover { background-color: var(--bs-secondary-active-bg, #ced4da); }
#last-viewed-popup #clear-last-viewed { color: var(--bs-danger-color, #dc3545); background-color: var(--bs-danger-bg, #f8d7da); border-color: var(--bs-danger-border, #f5c2c7); }
#last-viewed-popup #clear-last-viewed:hover { background-color: var(--bs-danger-active-bg, #f1aeb5); }
#last-viewed-popup::-webkit-scrollbar { width: 7px; height: 7px; background-color: #e6f9ff; border-radius: 4px; }
#last-viewed-popup::-webkit-scrollbar-thumb { background: #99ceff; border-radius: 100px; }
#last-viewed-popup::-webkit-scrollbar:hover { width: 12px; }
/* Search box styles */
#last-viewed-search-container { flex-shrink: 0; margin-bottom: 15px; }
#last-viewed-search { width: 100%; padding: 0.375rem 0.75rem; font-size: 1rem; font-weight: 400; line-height: 1.5; color: var(--bs-body-color); background-color: var(--bs-body-bg); background-clip: padding-box; border: 1px solid var(--bs-border-color); appearance: none; border-radius: var(--bs-border-radius); transition: border-color .15s ease-in-out,box-shadow .15s ease-in-out; }
#last-viewed-search:focus { border-color: #86b7fe; outline: 0; box-shadow: 0 0 0 0.25rem rgba(13,110,253,.25); }
#last-viewed-search::placeholder { color: #6c757d; opacity: 1; }
#last-viewed-no-results { text-align: center; color: #6c757d; margin-top: 1rem; display: none; }
/* Dynamic Tooltip Styles */
#last-viewed-dynamic-tooltip{position:fixed;background-color:#212529;color:#fff;padding:2px 10px;border-radius:6px;font-size:16px;font-weight:150;font-family:"Assistant",sans-serif;white-space:nowrap;z-index:1100;pointer-events:none;--arrow-size:6px;}
#last-viewed-dynamic-tooltip::before{content:"";position:absolute;top:50%;left:100%;margin-top:calc(-1*var(--arrow-size));border-width:var(--arrow-size);border-style:solid;border-color:transparent transparent transparent #212529;}
`;
GM_addStyle(styles);
function getLastViewed() { const data = GM_getValue(STORAGE_KEY, '[]'); try { return JSON.parse(data); } catch (e) { console.error("[LastViewed] Error parsing stored data:", e); return []; } }
function setLastViewed(items) { const limitedItems = items.slice(-MAX_HISTORY_ITEMS); GM_setValue(STORAGE_KEY, JSON.stringify(limitedItems)); }
function addTopicToHistory(topicTitle, topicUrl) {
if (!topicTitle || !topicUrl) return;
topicTitle = topicTitle.trim();
const normalizedUrl = topicUrl.split('?')[0].split('#')[0];
let items = getLastViewed();
const existingIndex = items.findIndex(item => item && item.url === normalizedUrl && item.title === topicTitle);
if (existingIndex !== -1) {
items[existingIndex].timestamp = Date.now();
} else {
items = items.filter(item => item && item.url !== normalizedUrl || item.title !== topicTitle);
items.push({ title: topicTitle, url: normalizedUrl, timestamp: Date.now() });
}
setLastViewed(items);
}
function getTopicDetailsFromPage() {
const currentUrl = window.location.href;
if (!currentUrl.startsWith(TOPIC_URL_PREFIX)) {
return null;
}
let title = '';
let $titleSpan = $('h1[component="post/header"] span[component="topic/title"]');
if ($titleSpan.length > 0) {
let $tempSpan = $titleSpan.clone();
$tempSpan.find('span[style*="font-size: 10px"]').remove();
title = $tempSpan.text().replace(/\s+/g, ' ').trim();
}
if (!title) {
title = document.title.replace(/ \| מתמחים טופ.*/, '').trim();
}
const normalizedUrl = currentUrl.split('?')[0].split('#')[0];
return { title: title, url: normalizedUrl };
}
let debounceTimer; function debounce(func, delay) { return function(...args) { clearTimeout(debounceTimer); debounceTimer = setTimeout(() => { if (typeof func === 'function') { func.apply(this, args); } else { console.error("[LastViewed] Debounced function is not valid at time of execution."); } }, delay); }; }
const saveCurrentTopicDebounced = debounce(function() { const currentTopic = getTopicDetailsFromPage(); if (currentTopic && currentTopic.title) { addTopicToHistory(currentTopic.title, currentTopic.url); } }, DEBOUNCE_DELAY);
function closePopupAndRemoveListener() { const $popup = $('#last-viewed-popup'); if ($popup.length) { $(document).off('click.closeLastViewed'); $popup.remove(); } }
function addClosePopupListener() { $(document).off('click.closeLastViewed'); $(document).on('click.closeLastViewed', function(event) { if (!$('#last-viewed-popup').length) { $(document).off('click.closeLastViewed'); return; } if (!$(event.target).closest('#last-viewed-popup').length && !$(event.target).closest('#last-viewed-btn-li').length) { closePopupAndRemoveListener(); } }); }
function filterLastViewedList() { const searchTerm = $('#last-viewed-search').val().toLowerCase().trim(); const $listItems = $('#last-viewed-list li'); let visibleCount = 0; $listItems.each(function() { const $item = $(this); const itemText = $item.find('a').text().toLowerCase(); if (itemText.includes(searchTerm)) { $item.show(); visibleCount++; } else { $item.hide(); } }); if (visibleCount === 0 && $listItems.length > 0) { $('#last-viewed-no-results').show(); } else { $('#last-viewed-no-results').hide(); } }
function showLastViewedPopup() {
if ($('#last-viewed-popup').length) return;
closePopupAndRemoveListener();
console.log("[LastViewed] Opening popup.");
let items = getLastViewed();
const uniqueItems = [];
const seenTitles = new Set();
for (let i = items.length - 1; i >= 0; i--) {
const item = items[i];
if (!seenTitles.has(item.title)) {
uniqueItems.push(item);
seenTitles.add(item.title);
}
}
items = uniqueItems;
const popup = document.createElement('div');
popup.id = 'last-viewed-popup';
const searchContainer = document.createElement('div');
searchContainer.id = 'last-viewed-search-container';
const searchInput = document.createElement('input');
searchInput.type = 'search';
searchInput.id = 'last-viewed-search';
searchInput.placeholder = 'חיפוש בנצפו לאחרונה...';
searchInput.className = 'form-control';
searchContainer.appendChild(searchInput);
popup.appendChild(searchContainer);
const title = document.createElement('h2');
title.textContent = 'שרשורים שנצפו לאחרונה';
popup.appendChild(title);
if (items.length === 0) {
const emptyMsg = document.createElement('p');
emptyMsg.textContent = 'לא נצפו שרשורים לאחרונה.';
popup.appendChild(emptyMsg);
} else {
const list = document.createElement('ul');
list.id = 'last-viewed-list';
for (let i = 0; i < items.length; i++) {
const item = items[i];
const listItem = document.createElement('li');
const link = document.createElement('a');
link.href = item.url;
link.textContent = item.title || 'ללא כותרת';
link.target = '_blank';
listItem.appendChild(link);
list.appendChild(listItem);
}
popup.appendChild(list);
const noResultsMsg = document.createElement('p');
noResultsMsg.id = 'last-viewed-no-results';
noResultsMsg.textContent = 'לא נמצאו שרשורים תואמים.';
popup.appendChild(noResultsMsg);
}
const controlsDiv = document.createElement('div');
controlsDiv.className = 'popup-controls';
const clearButton = document.createElement('button');
clearButton.textContent = 'נקה היסטוריה';
clearButton.id = 'clear-last-viewed';
controlsDiv.appendChild(clearButton);
const closeButton = document.createElement('button');
closeButton.textContent = 'סגור';
closeButton.className = 'close-popup-btn';
controlsDiv.appendChild(closeButton);
popup.appendChild(controlsDiv);
document.body.appendChild(popup);
$('#clear-last-viewed').on('click', function(e) { e.stopPropagation(); if (confirm('האם אתה בטוח שברצונך למחוק את היסטוריית הצפייה?')) { GM_deleteValue(STORAGE_KEY); closePopupAndRemoveListener(); console.log("[LastViewed] History cleared."); } });
$('.close-popup-btn').on('click', function(e) { e.stopPropagation(); closePopupAndRemoveListener(); });
$('#last-viewed-search').on('input', filterLastViewedList);
setTimeout(addClosePopupListener, 50);
$('#last-viewed-search').trigger('focus');
}
let currentTooltip = null; const TOOLTIP_TEXT = "נצפו לאחרונה"; const TOOLTIP_GAP = -88.5; function showDynamicTooltip(targetElement) {
const sidebar = document.querySelector('nav[component="sidebar/left"]');
if (sidebar && sidebar.classList.contains('open')) {
return;
}
removeDynamicTooltip(); currentTooltip = document.createElement('div'); currentTooltip.id = 'last-viewed-dynamic-tooltip'; currentTooltip.textContent = TOOLTIP_TEXT; document.body.appendChild(currentTooltip); const rect = targetElement.getBoundingClientRect(); const tooltipRect = currentTooltip.getBoundingClientRect(); let top = rect.top + (rect.height / 2) - (tooltipRect.height / 2); let left = rect.left + rect.width + TOOLTIP_GAP - 58; if (top < 5) top = 5; if (top + tooltipRect.height > window.innerHeight - 5) top = window.innerHeight - tooltipRect.height - 5; if (left + tooltipRect.width > window.innerWidth - 5) left = window.innerWidth - tooltipRect.width - 5; currentTooltip.style.top = `${top}px`; currentTooltip.style.left = `${left}px`; currentTooltip.style.right = 'auto'; } function removeDynamicTooltip() { if (currentTooltip) { currentTooltip.remove(); currentTooltip = null; } }
function createSidebarButton() {
console.log("[LastViewed] Attempting createSidebarButton execution...");
if ($('#last-viewed-btn-li').length > 0) { console.log("[LastViewed] Button LI already exists."); return; }
console.log("[LastViewed] Selecting navList...");
const $navList = $('nav[component="sidebar/left"] ul#main-nav');
console.log(`[LastViewed] $navList length: ${$navList.length}`);
if ($navList.length === 0) { console.error("[LastViewed] FAILED to find $navList."); return; } // Exit if navList not found
console.log("[LastViewed] Selecting recentPostsItem...");
const $recentPostsItem = $navList.find('li a[href="/recent"]').closest('li');
console.log(`[LastViewed] $recentPostsItem length: ${$recentPostsItem.length}`);
if ($recentPostsItem.length === 0) { console.error("[LastViewed] FAILED to find $recentPostsItem."); return; } // Exit if recent post item not found
console.log("[LastViewed] Creating $newItem element...");
const $newItem = $(`
<li class="nav-item mx-2" id="last-viewed-btn-li" data-bs-original-title="נצפו לאחרונה">
<a class="nav-link navigation-link d-flex gap-2 justify-content-between align-items-center" href="#" role="button" id="last-viewed-btn" aria-label="נצפו לאחרונה">
<span class="d-flex gap-2 align-items-center text-nowrap truncate-open">
<span class="position-relative">
<i class="fa fa-fw fa-history" data-content=""></i>
<span component="navigation/count" class="visible-closed position-absolute top-0 start-100 translate-middle badge rounded-1 bg-primary hidden"></span>
</span>
<span class="nav-text small visible-open fw-semibold text-truncate">נצפו לאחרונה</span>
</span>
<span component="navigation/count" class="visible-open badge rounded-1 bg-primary hidden"></span>
</a>
</li>
`);
console.log("[LastViewed] Attaching listeners to $newItem...");
$newItem.find('a#last-viewed-btn').on('click', function(event) { event.preventDefault(); event.stopPropagation(); removeDynamicTooltip(); showLastViewedPopup(); });
$newItem.on('mouseenter', function(event) { showDynamicTooltip(event.currentTarget); }).on('mouseleave', function() { removeDynamicTooltip(); });
try {
console.log("[LastViewed] Attempting insertBefore...");
$newItem.insertBefore($('li[data-bs-original-title="נושאים שלא נפתרו"]'));
setTimeout(() => {
if ($('#last-viewed-btn-li').length > 0) {
console.log("[LastViewed] Button inserted successfully (verified).");
} else {
console.error("[LastViewed] Button insertion failed silently (not found after insertBefore).");
}
}, 50);
} catch (e) {
console.error("[LastViewed] Error during button insertion:", e);
}
}
let observer = null;
function setupMutationObserver() {
const targetNode = document.getElementById('content'); if (!targetNode) { console.error("[LastViewed] Cannot find target node '#content'. Retrying..."); setTimeout(setupMutationObserver, 1000); return; } const config = { childList: true, subtree: true }; const callback = function(mutationsList, observer) { let titleSpanFound = false; for(const mutation of mutationsList) { if (mutation.type === 'childList') { mutation.addedNodes.forEach(node => { if (node.nodeType === Node.ELEMENT_NODE) { if ($(node).find('span[component="topic/title"]').length > 0 || $(node).is('span[component="topic/title"]')) { titleSpanFound = true; } } }); } if (titleSpanFound) break; } if (titleSpanFound) { saveCurrentTopicDebounced(); } }; observer = new MutationObserver(callback); observer.observe(targetNode, config); const parentObserver = new MutationObserver(mutations => { mutations.forEach(mutation => { mutation.removedNodes.forEach(removedNode => { if (removedNode.id === 'content') { console.warn("[LastViewed] Target node #content was removed. Re-initializing observer."); if(observer) observer.disconnect(); setTimeout(setupMutationObserver, 50); } }); }); }); if (targetNode.parentNode) { parentObserver.observe(targetNode.parentNode, { childList: true }); }
}
function initialize() {
console.log("[LastViewed] Initializing script v2.0.1 (Increased Timeout)...");
if (typeof createSidebarButton !== 'function' || typeof saveCurrentTopicDebounced !== 'function' || typeof setupMutationObserver !== 'function') {
console.error("[LastViewed] CRITICAL: One or more core functions not defined!"); return;
}
console.log("[LastViewed] Calling createSidebarButton...");
createSidebarButton();
console.log("[LastViewed] Calling saveCurrentTopicDebounced (initial)...");
saveCurrentTopicDebounced();
console.log("[LastViewed] Calling setupMutationObserver...");
setupMutationObserver();
console.log("[LastViewed] Initialization complete.");
}
ensureDependencies(initialize);
})();