您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
מעולם לא היה קל יותר לגלוש ב-FxP.
// ==UserScript== // @name BetterFxP for Tampermonkey // @namespace http://tampermonkey.net/ // @version 1.7 // @description מעולם לא היה קל יותר לגלוש ב-FxP. // @author You // @match https://www.fxp.co.il/* // @supportURL https://discord.gg/AW7CeG7 // @require https://update.greasyforks.org/scripts/439099/1203718/MonkeyConfig%20Modern%20Reloaded.js // @icon https://lh3.googleusercontent.com/j-CdJwaXX0eoqlMDLLYfbYTuuaFUM5Ep-Mph1UNktCZSYbm665WoIwGGw4d1iXxQWkLMDYior_xS8OKfWCBf1i4srw=s120 // @grant GM_getValue // @grant GM_setValue // @grant GM_addStyle // @grant GM_addElement // @grant GM_getResourceText // @grant GM_registerMenuCommand // @grant GM_addValueChangeListener // @run-at document-start // @resource pms https://update.greasyforks.org/scripts/476628/1259426/fxp%20anti-delete%20PMs.user.js // @license MIT // ==/UserScript== // @noframes const checkbox = (label) => ({ label, default: false, type: 'checkbox' }); const text = (label, defaultValue = '', opt = {}) => ({ label, default: defaultValue, type: 'text', ...opt }); const cfg = new MonkeyConfig({ title: 'הגדרות FxPlus+', menuCommand: true, params: { hideBigImages: checkbox("הסתר את הכתבות הגדולות מדך הבית"), hideGames: checkbox("הסתר את אזור המשחקים מדף הבית"), hideArticles: checkbox("הסתר כתבות מדף הבית"), resizeSignatures: checkbox("חותך חתימות גדולות"), hideAds: checkbox("הסתר מודעות"), hideNagish: checkbox("הסתר את תפריט הנגישות"), showFriends: checkbox("הצג חברים באשכולת"), showAutoPinned: checkbox("הצג יותר משלושה אשכולות נעוצים"), disableLiveTyping: checkbox("אל תודיע שאני מקליד"), showDeletedPost: checkbox("הצג פוסט שנמחק"), showLikeLimit: checkbox("הצג מגבלת לייקים"), connectedStaff: checkbox("הצג צוות מחובר"), showCounts: checkbox('מציג את מספר הפוסטים ואת כמות המשתמשים המחוברים'), pms: checkbox("מציג הודעות פרטיות שנמחקו"), showForumStats: checkbox('הצג סטטיסטיקות פורומים'), weeklyChallenge: checkbox('מציג אתגרים שבועיים בתוך הפורום'), audioChange: text(":קישור לקובץ שמע עבור התראה"), // https://www.tzevaadom.co.il/static/sounds/calm.wav hideCategories: text(":רשימה של קטגוריות להסתרה"), // 4428, 13 smiles: text(':רשימה של קישורים לסמיילים', '', { long: 3 }), // https://yoursmiles.org/tsmile/heart/t4524.gif nightMode: checkbox('הפעל את מצב הלילה אוטומטית'), startTime: text('Start Time:', '17:00'), endTime: text("End Time:", '23:50'), color: text("צבע"), font: text('פונט'), size: text('גודל'), // This is a temporary solution until I find a better library. // TODO: md5 the pass user1name: text("שם"), user1pass: text("סיסמה"), user2name: text("שם"), user2pass: text("סיסמה"), } }); document.addEventListener('keydown', function (e) { if (e.ctrlKey && e.key.toLowerCase() === 'y') { e.preventDefault(); cfg.open("window", { windowFeatures: { width: 500 } }); } }); /* CKEDITOR.tools.callFunction(41, this); //131,'almoni-dl' The system that automatically disables and enables the feature is currently not working in either version. TODO: - Implement audio file upload - disable/enable on the same page and prevent reload - BBCode support - Hide sticky posts */ //<a class="report" href="javascript:alert(123)" style="background-image: url('data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' font-size='15' text-anchor='middle' dominant-baseline='central'><text x='50%' y='50%'>%E2%9A%A0%EF%B8%8F</text></svg>');"></a> // `[CENTER][FONT=open sans hebrew][SIZE=5]דיווח קריטי // [/SIZE][SIZE=3]XXX // [/SIZE][/FONT][/CENTER] // [FONT=open sans hebrew][B]א. קישור לפרופיל המשתמש של פותח האשכול: // [/B]https://www.fxp.co.il/member.php?u=XX // [B]ב. תאריך לידה של המשתמש: // [/B]אין תאריך בפרופיל או התאריך // [B]ג. קישור לאשכול עצמו: // [/B]https://www.fxp.co.il/showthread.php?t=xx // [B]ד. תאריך ושעת פתיחת האשכול: // [/B]להוסיף // [B]ה. מתי התחבר המשתמש בפעם האחרונה: // [/B]תאריך // [B]ו. כתובת מייל של המשתמש: // [/B]צריך לשנות!! // [B]ז. קישור לתמונת תצלום מסך של האשכול: [/B][/FONT][FONT=open sans hebrew][SIZE=3][CENTER][COLOR=#D7DADC][FONT="][/FONT][/COLOR][/CENTER][/SIZE][/FONT] // ` const rawWindow = unsafeWindow; const queryParams = new URLSearchParams(location.search); function waitForObject(path) { return new Promise((resolve, reject) => { const timer = setInterval(() => { const obj = path.split('.').reduce((o, key) => (o && key in o ? o[key] : undefined), rawWindow); if (typeof obj !== "undefined") { clearInterval(timer); resolve(obj); } }, 100); }); } function setCookie(name, value, minutes) { const expires = new Date(Date.now() + minutes * 60 * 1000).toUTCString(); document.cookie = `${name}=${encodeURIComponent(value)}; expires=${expires}; path=/`; } function getCookie(n) { return decodeURIComponent(document.cookie.split('; ').find(c => c.startsWith(n + '='))?.split('=')[1] || ''); } function onMatchIfLoggedIn(match, permissions, callback) { rawWindow.LOGGEDIN && onMatch(match, permissions, callback); } function onMatch(match, permission, callback) { const hasPermission = permission === "none" || cfg.get(permission); if (!shouldRun(match) || !hasPermission) return; const docReady = /complete|interactive/.test(document.readyState); const runImmediately = !callback.toString().includes("document"); let teardown = () => {}; const executeFeature = () => { teardown(); teardown = callback() || (() => {}); }; if (runImmediately || docReady) executeFeature(); else document.addEventListener('DOMContentLoaded', executeFeature); GM_addValueChangeListener(permission, (key, oldVal, newVal) => { const func = newVal ? executeFeature : teardown; func(); }); } function injectStyle(match, permissions, css) { onMatch(match, permissions, function() { const styleElement = GM_addStyle(css); return () => styleElement?.remove(); }); } function shouldRun(matchPattern) { const urlPath = '/' + location.href.split('/').pop(); const pattern = new RegExp(matchPattern.replace('*', '.*')); return pattern.test(urlPath); } async function fetcher(url, opt = {}) { const response = await fetch(url, opt); return await response.text(); } function Listener(callback) { const originalOpen = XMLHttpRequest.prototype.open; const originalSend = XMLHttpRequest.prototype.send; XMLHttpRequest.prototype.open = function(method, url) { this.method = method; this.url = url; originalOpen.apply(this, arguments); }; XMLHttpRequest.prototype.send = function(body) { this.body = body; this.addEventListener("load", () => { callback(this); }); originalSend.apply(this, arguments); }; } // Author ID: 967488 injectStyle("forumdisplay", "showAutoPinned", "#stickies li.threadbit:nth-child(n+4) { display: list-item !important; } .morestick { display: none !important; }"); injectStyle("*", "hideAds", "#adfxp, #related_main, .trc_related_container, .trc_spotlight_widget, .videoyoudiv, .OUTBRAIN { display: none !important }"); injectStyle("*", "hideNagish", ".nagish-button { display: none; }"); injectStyle("/(?:index.php)?", "hideCategories", `${cfg.get("hideCategories").split(", ").map(cId => `.hp_category:has(a[href="forumdisplay.php?f=${cId}"])`).join(',')} { display: none }`) // #cat${cId}, .hi4 { height: 337px } injectStyle("/(?:index.php)?", "hideArticles", "#slide { height:auto !important; } .mainsik { display: none; }"); injectStyle("/(?:index.php)?", "hideBigImages", "#slide { height:auto !important; } .big-image-class { display: none; }"); injectStyle("/(?:index.php)?", "hideGames", "#slide ~ div h1, .fxp2021_Games { display: none !important;"); // חותך חתימות גדולות לגודל המותר וזה תלוי במשתמש במקום באתר לשנות את החתימה שתתאים injectStyle("show(post|thread)|member.php", "resizeSignatures", ".signaturecontainer { max-width: 500px; max-height: 295px; overflow: hidden; }"); onMatchIfLoggedIn("private_chat.php?do=showpm|show(post|thread)", "disableLiveTyping", function() { const originalSendTypingInThread = rawWindow?.sendUserIsTypingInShowthread; const originalTypingSend = rawWindow?.typeingsend; rawWindow.sendUserIsTypingInShowthread = () => {}; rawWindow.typeingsend = () => {}; return () => { rawWindow.sendUserIsTypingInShowthread = originalSendTypingInThread; rawWindow.typeingsend = originalTypingSend; } }); onMatchIfLoggedIn("signature", "none", function() { const publishedThreadUrl = "https://www.fxp.co.il/showthread.php?t=16859147"; GM_addStyle(` #creditAddon { padding: .5em; text-align: center; } .addCreditBtn { margin: .2em 1em; background: #fff; border-radius: .2em; font-weight: 700; color: #004b67; border: 1px solid #c5c5c5; cursor: pointer; padding: 0; position: relative; width: 60px; height: 60px; display: inline-flex; justify-content: center; align-items: center; overflow: hidden; } .addCreditBtn img { border: 0; height: 100%; } .addCreditBtn .addCreditDesc { position: absolute; left: 0; top: 0; width: 100%; height: 100%; display: flex; justify-content: center; align-items: center; color: #fff; background: rgba(0,0,0,.4); opacity: 1; } .addCreditBtn .addCreditDesc:hover { opacity: 0; } .addCreditBtn#addTextCredit .addCreditDesc:hover { opacity: 1; } .addCreditBtn#addXLimg { width: 110px; } `); const creditAddon = GM_addElement('div', { id: 'creditAddon', }) creditAddon.innerHTML = ` <div>שתף את הכיף!™ והוסף קרדיט לתוסף +FxPlus בחתימה שלך:</div> <div class="addCreditBtn" id="addLimg"> <img src="https://i.imagesup.co/images2/b059514f80af8c5ec69afc73356a4dfa3b771343.png"> <span class="addCreditDesc">128x128</span> </div> <div class="addCreditBtn" id="addMimg"> <img src="https://i.imagesup.co/images2/7797f421f0e4895878e51d09266a35355b214d5a.png"> <span class="addCreditDesc">48x48</span> </div> <div class="addCreditBtn" id="addTextCredit"> <span class="addCreditDesc">טקסט</span> </div>` creditAddon.querySelectorAll('#addLimg, #addMimg, #addTextCredit').forEach(element => { element.addEventListener('click', (event) => { const imgElement = event.target.parentElement.querySelector('img'); const iframeBody = document.querySelector(".cke_contents iframe").contentDocument.body; const creditLink = GM_addElement(iframeBody, 'a', { href: publishedThreadUrl, target: '_blank' }); if (!imgElement) creditLink.textContent = '+FxPlus'; else GM_addElement(creditLink, "img", { src: imgElement.src }); }); }); const smilieBox = document.querySelector('form[action*="signature"] .editor_smiliebox'); smilieBox.parentNode.insertBefore(creditAddon, smilieBox); }); onMatchIfLoggedIn("*", "showFriends", async function () { const storageKey = "refreshFriends"; if (getCookie(storageKey)) return; const allFriendIds = []; const regex = /<h4><a href="member\.php\?u=(\d+)"/g; let page = 1; while (true) { const url = `https://www.fxp.co.il/profile.php?do=buddylist&pp=100&page=${page}`; const html = await fetcher(url); const matches = Array.from(html.matchAll(regex)).map(m => m[1]); matches.forEach(id => allFriendIds.push(id)); if (matches.length < 100) break; page++; } GM_setValue('friendIds', JSON.stringify(allFriendIds)); setCookie(storageKey, Date.now(), 15); }); onMatchIfLoggedIn("show(post|thread)", "showFriends", function() { const friendIds = JSON.parse(GM_getValue("friendIds", '[0]')); const styleElement = GM_addStyle(` ${friendIds.map(id => '.username[href$="' + id + '"]::after').join(', ')} { content: ""; display: inline-block; width: 20px; height: 20px; background-image: url('https://w7.pngwing.com/pngs/236/25/png-transparent-computer-icons-avatar-friends-love-text-logo-thumbnail.png'); background-size: cover; }`) return () => styleElement?.remove() }) onMatchIfLoggedIn("*", "audioChange", function() { let isFeatureEnabled = true; Object.defineProperty(HTMLAudioElement.prototype, 'src', { set: function(value) { if (isFeatureEnabled && value === "https://images4.fxp.co.il/nodejs/sound.mp3") { value = cfg.get("audioChange"); } this.setAttribute('src', value); }, get: () => this.getAttribute('src'), configurable: true, enumerable: true, }); return () => { isFeatureEnabled = false; } }) onMatchIfLoggedIn("show(post|thread)", "smiles", async function() { const images = cfg.get("smiles").trim().split('\n'); if (images.length < 1) return; const editor = await waitForObject("vB_Editor.vB_Editor_QR"); let originalDescriptions = editor.config.smiley_descriptions, originalImages = editor.config.smiley_images; for (const image of images) { if (!image) continue; editor.config.smiley_descriptions.push(`[img]${image}[/img]`); editor.config.smiley_images.push(`https://wsrv.nl/?url=${image}&w=30`); } return () => { editor.config.smiley_descriptions = originalDescriptions; editor.config.smiley_images = originalImages; } }) onMatchIfLoggedIn("show(post|thread)", "showLikeLimit", async function() { let toRemove = []; async function checkLike(postid) { const response = await fetcher("https://www.fxp.co.il/ajax.php", { method: "POST", headers: { "content-type": "application/x-www-form-urlencoded", }, body: `do=wholikepost&postid=${postid}&securitytoken=${rawWindow.SECURITYTOKEN}`, }); return response.includes(`>${rawWindow.my_user_name}<`); } Listener(async e => { if (e.method !== "POST" || e.url !== "ajax.php") { return; } const postId = e.body.match(/\d+/); if (!postId || await checkLike(postId)) return; const element = document.getElementById(`${postId}_removelike`); element.style.backgroundImage = 'url("https://em-content.zobj.net/source/google/387/broken-heart_1f494.png")'; toRemove.push(element); }); return () => { toRemove.forEach(el => el.style.backgroundImage = ''); toRemove = []; } }) // https://greasyforks.org/en/scripts/476628-fxp-anti-delete-pms onMatchIfLoggedIn("do=showpm&pmid=", "pms", async function() { await waitForObject("socket"); new Function(GM_getResourceText("pms")).apply(rawWindow); }) onMatch("show(post|thread)", "showDeletedPost", function() { const targetPostId = queryParams.get('p'); const isPostExist = document.contains(document.getElementById('post_' + targetPostId)); if (!targetPostId || isPostExist) return; const elements = Array.from(document.querySelectorAll('.postbit')); const postIds = elements.map(el => parseInt(el.id.replace('post_', ''))); const index = postIds.filter(pid => pid < targetPostId).length - 1; // if (index === 0) index = 1; //need to be test const newElement = GM_addElement("li", { textContent: 'התגובה שאתה מנסה לראות נמחקה.', id: 'post_' + targetPostId, class: 'postbit postbitim postcontainer', //test how this show (innerHTML) style: "background-color: #ffdddd; border: 1px solid #ff0000; padding: 10px 0; border-radius: 5px; color: #333; font-weight: bold; text-align: center;" }) const targetElement = elements.at(index); targetElement.parentNode.insertBefore(newElement, targetElement.nextSibling); if (!queryParams.has('t')) { setTimeout(() => targetElement.scrollIntoView(), 500); } return () => newElement?.remove(); }); onMatch("forumdisplay", "connectedStaff", function() { const team = document.querySelector(".teammen.flo"); team.dir = "auto" const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); svg.setAttribute('width', '10px'); svg.setAttribute('height', '10px'); svg.setAttribute('viewBox', '0 0 24 24'); const path = document.createElementNS('http://www.w3.org/2000/svg', 'path'); path.setAttribute('fill', '#00FF00'); path.setAttribute('d', 'm2 12a10 10 0 1 1 10 10 10 10 0 0 1 -10-10z'); svg.appendChild(path); const usernameElements = document.querySelectorAll('.flo .username'); usernameElements.forEach(async usernameElement => { const username = usernameElement.innerText; const userLink = usernameElement.href; const html = await fetcher(userLink); if (html.includes(username + ' מחובר/ת')) { usernameElement.insertAdjacentHTML('beforeend', svg.outerHTML); } }); return () => { team.dir = ""; usernameElements.forEach(({ lastChild }) => lastChild.tagName === 'svg' && lastChild.remove()); } }); onMatch("*", "showCounts", function() { const scripts = document.querySelectorAll('script[type="text/javascript"]:not([src])'); const script = Array.from(scripts).find(e => e.textContent.includes("counts")); if (!script) return; const lines = script.innerText.split("\n"); const counts = {}; for (const line of lines) { const match = line.match(/counts\["(.+?)"\]\s*=\s*(\d+);/); if (match) counts[match[1]] = parseInt(match[2]); } const container = GM_addElement("div", { style: ` position: fixed; bottom: 10px; right: 10px; background: white; color: white; border: 1px solid #ccc; padding: 10px 15px; box-shadow: 0 0 5px rgba(0,0,0,0.2); border-radius: 8px; z-index: 9999; font-family: Arial, sans-serif; font-size: 14px; transition: opacity 1s` }); container.innerHTML = ` <strong>סטטיסטיקה:</strong><br> מחוברים: ${counts["#total_online"]?.toLocaleString() ?? 'N/A'}<br> פוסטים: ${counts["#total_posts"]?.toLocaleString() ?? 'N/A'} `; setTimeout(() => { container.style.opacity = "0"; container.addEventListener("transitionend", container.remove); }, 5000); }); //TODO: still needs improvement, but much better than before onMatch("forumdisplay", "showForumStats", function() { function getDupeSortedDictionary(arr) { const counts = new Map(); for (const item of arr) counts.set(item, (counts.get(item) || 0) + 1); const sortedArr = Array.from(counts, ([value, count]) => ({ value, count })); sortedArr.sort((a, b) => { return b.count === a.count ? a.value.localeCompare(b.value) : b.count - a.count; }); return sortedArr; } function openPopupWindow(title, content) { let dialog = document.getElementById("detailedStats"); if (!dialog) { dialog = GM_addElement("dialog", { id: "detailedStats" }) dialog.innerHTML = `${title}${content}` dialog.addEventListener("click", (e) => { if (e.target === dialog) dialog.close(); }); } dialog.showModal(); } function removePopupWindow(id) { const dialog = document.getElementById(id); if (dialog && dialog.open) { dialog.close(); } } const total = document.querySelectorAll('#threads .threadtitle').length; const container = document.createElement("div"); container.id = "forumStatsContainer"; const threadsList = document.querySelector(".threads_list_fxp"); threadsList?.parentNode?.insertBefore(container, threadsList.nextSibling); const forumStats = GM_addElement(container, "div"); GM_addElement(forumStats, "i", { textContent: `נתונים סטטיסטיים של ${total} אשכולות:` }); const toArray = selector => Array.from(document.querySelectorAll(selector)).map(el => el.textContent); function appendLine(dict, introText, noText, suffix) { const line = GM_addElement(forumStats, "div"); if (dict.length > 1 && dict[0].count > 1) { line.append(introText); for (let i = 0; i < dict.length && dict[i].count === dict[0].count; i++) { if (i > 0) line.append(" או "); GM_addElement(line, "b", { textContent: dict[i].value }); } line.append(" עם " + dict[0].count + suffix); } else { line.append(noText); } } const publishersDict = getDupeSortedDictionary(toArray("#threads .threadinfo .username")); const commentorsDict = getDupeSortedDictionary(toArray("#threads .threadlastpost .username")); const prefixesDict = getDupeSortedDictionary(toArray("#threads .prefix").map(prefix => prefix.replace(/\||סקר: /g, ''))); appendLine( publishersDict, "המפרסם הדומיננטי ביותר הוא ", "אין מפרסם דומיננטי במיוחד.", " אשכולות." ); appendLine( commentorsDict, "המגיב האחרון הדומיננטי ביותר הוא ", "אין מגיב אחרון דומיננטי במיוחד.", " תגובות אחרונות." ); appendLine( //TODO: שנמצא ב-" + prefixesDict[0].count + " אשכולות. prefixesDict, "התיוג הנפוץ ביותר הוא ", "אין תיוג נפוץ במיוחד.", " אשכולות." ); const parseNumber = text => parseInt(text.replace(/[^\d]/g, ""), 10) || 0; let commentsCount = 0; let viewsCount = 0; document.querySelectorAll("#threads .threadstats").forEach(el => { const [comments, views] = el.querySelectorAll("li"); commentsCount += parseNumber(comments.textContent); viewsCount += parseNumber(views.textContent); }); let viewsCommentsRatio = commentsCount > 0 ? Math.max(1, Math.round(viewsCount / commentsCount)) : "∞"; const ratioLine = document.createElement("div"); const b = document.createElement("b"); b.textContent = viewsCommentsRatio + " צפיות"; ratioLine.append("יחס הצפיות לתגובה הוא תגובה כל ", b, "."); forumStats.appendChild(ratioLine); const detailedStatsBtn = GM_addElement(forumStats, "div", { textContent: "+" }); detailedStatsBtn.addEventListener("click", () => { const pContent = document.createElement("div"); const flexTableContainer = GM_addElement(pContent, "div", { style: "display: flex; flexWrap: wrap;" }); function helper(headerA, headerB, arr) { const table = GM_addElement(flexTableContainer, "table"); const headerRow = GM_addElement(table, "tr") GM_addElement(headerRow, "th", { textContent: headerA }); GM_addElement(headerRow, "th", { textContent: headerB }); arr.forEach(item => { const tr = GM_addElement(table, "tr"); GM_addElement(tr, "td", { textContent: item.value }); GM_addElement(tr, "td", { textContent: item.count }); }); } helper("מפרסם", "אשכולות", publishersDict); helper("מגיב", "תגובות אחרונות", commentorsDict); helper("תיוג", "אשכולות", prefixesDict); const closeBtn = GM_addElement(pContent, "div", { textContent: "סגור" }); closeBtn.addEventListener("click", () => removePopupWindow("detailedStats")); const forumTitle = document.querySelector('.lastnavbit > span').textContent; openPopupWindow( "סטטיסטיקות מפורטות לפורום " + forumTitle, pContent.outerHTML ); }); return () => { document.querySelectorAll("#forumStatsContainer, dialog").forEach(e => e.remove()); }; }); onMatch("*", "nightMode", function () { const toggleDarkMode = (isEnabled) => setCookie("bb_darkmode", isEnabled ? "1" : "0", 1440); function timeInMinutes(timeString) { if (!timeString) return 0; const [hours, minutes] = timeString.split(':').map(Number); return hours * 60 + minutes; } const darkModeThemeEl = document.querySelector("#darkmode_theme"); function exec() { const now = new Date(); const minutesCurrent = now.getHours() * 60 + now.getMinutes(); const minutesStart = timeInMinutes(cfg.get("startTime")); const minutesEnd = timeInMinutes(cfg.get("endTime")); const rangeActive = minutesEnd < minutesStart ? (minutesCurrent >= minutesStart || minutesCurrent < minutesEnd) : (minutesCurrent >= minutesStart && minutesCurrent < minutesEnd); const nightModeActive = getCookie("bb_darkmode") == "1" if (nightModeActive && !rangeActive) { darkModeThemeEl?.classList?.remove('ofset'); document.body.classList.remove('darkmode'); document.querySelector('[href*="darkmode"]')?.remove(); toggleDarkMode(false); } else if (!nightModeActive && rangeActive) { darkModeThemeEl?.classList?.add('ofset'); document.body.classList.add('darkmode'); if (!document.querySelector('[href*="darkmode"]')) { GM_addElement("link", { rel: 'stylesheet', href: '//static.fcdn.co.il/dyn/projects/css/desktop/darkmode.css' }); } toggleDarkMode(true); } }; exec(); const interval = setInterval(exec, 20 * 1000); return () => clearInterval(interval); }); onMatch("forumdisplay", "weeklyChallenge", async function() { const getTodayDMY = () => new Date().toLocaleDateString('en-GB').split('/').join('-'); function parseDMY(dateStr) { const [day, month, year] = dateStr.split('-').map(Number); return new Date(year, month - 1, day); } function checkDateDifference(dateStr1, dateStr2) { const date1 = parseDMY(dateStr1); const date2 = parseDMY(dateStr2); return Math.abs(date2 - date1) / 604800000; // 604800000 = week } const target = document.querySelector(".flo > .description_clean"); const container = GM_addElement(target, "div", { style: "direction: rtl; text-align: right; max-width: 300px; padding: 10px;" }); GM_addElement(container, "h3", { textContent: "אשכול השבוע", style: "margin-top: 0;" }); GM_addElement(container, "div", { id: "thread-week", textContent: "טוען...", style: "margin-bottom: 12px; color: #222;" }); GM_addElement(container, "h3", { textContent: "משתמש השבוע" }); GM_addElement(container, "div", { id: "member-week", textContent: "טוען...", style: "color: #222;" }); const errorBox = GM_addElement(container, "div", { id: "weekly-error", style: "color: red; margin-top: 10px; display: none;" }); const CACHE_KEY = "weeklyChallengeCache" + rawWindow.FORUM_ID_FXP; const cachedData = getCookie(CACHE_KEY); if (cachedData) { try { const { thread, member } = JSON.parse(cachedData); document.getElementById("thread-week").textContent = thread; document.getElementById("member-week").textContent = member; return; } catch (e) {} } const domParser = new DOMParser(); const stickies = Array.from(document.querySelectorAll('.stickies .threadinfo')) .filter(node => /אשכול השבוע|משקיען השבוע|7 ימי ווינר|משקיען ואשכול/.test(node.textContent)) .map(node => node.parentElement); if (stickies.length > 1 || stickies.length === 0) { container.style.display = "none"; return; // maybe in the future, this will handle values greater than 1 } const sticky = stickies.shift(); const element = sticky.querySelector('a.lastpostdate'); const url = element.href?.replace(/#post.*/, ''); if (!url) { errorBox.style.display = "block"; errorBox.innerHTML = 'האשכול השבועי לא מכיל אף הכרזה, יש לפנות ל<a href="https://www.fxp.co.il/forumdisplay.php?f=18" target="_blank">צוות תמיכה</a>'; return; } const time = element.parentElement.textContent; const date = time.split(" ").shift().replace(/אתמול|היום/, getTodayDMY()); if (checkDateDifference(getTodayDMY(), date) >= 2) { errorBox.style.display = "block"; errorBox.innerHTML = 'האשכול השבועי לא עודכן זמן רב, יש לפנות ל<a href="https://www.fxp.co.il/forumdisplay.php?f=18" target="_blank">צוות תמיכה</a>'; return; } const response = await fetcher(url + "&pp=1"); const doc = domParser.parseFromString(response, "text/html"); const threads = doc.querySelectorAll(".postcontent a[href*='showthread.php']"); const members = doc.querySelectorAll(".postcontent a[href*='member.php']"); /* TODO: - Handle multiple thread links properly current implementation only processes the first link and does not cover all cases. - Replace textContent below with real URL */ let thread = threads.length > 0 ? threads[0].textContent.trim().replace(/^"|"$/g, "") : "לא נמצא אשכול"; let member = members.length > 0 ? members[0].textContent.trim() : "לא נמצא משתמש"; document.getElementById("thread-week").textContent = thread; document.getElementById("member-week").textContent = member; setCookie(CACHE_KEY, JSON.stringify({ thread, member }), 5); }) // TODO: check that every editor is compatible // consider adding a check to validate the parameter // currently does not support setting changes // This code runs only once; executeCommand works better then onMatch("show(post|thread)|newreply", "none", async function() { const instances = await waitForObject("CKEDITOR.instances"); const editor = Object.values(instances)[0]; let content = editor.getData(); // document if (!content.trim()) { content = '\u200B'; } ['size', 'font', 'color'].forEach(style => { const value = cfg.get(style); if (!value) return; content = `[${style}=${value}]${content}[/${style}]`; }) editor.setData(content); }); //temporally onMatch("upload.php", 'none', function() { const user1 = [cfg.get("user1name"), cfg.get("user1pass")] if (user1.filter(Boolean).length) { const b = GM_addElement(document.querySelector(".back_image"), "button", { textContent: "התחבר ל-" + user1[0] }) b.addEventListener("click", async function() { await login(...user1) }); } const user2 = [cfg.get("user2name"), cfg.get("user2spass")] if (user2.filter(Boolean).length) { const b = GM_addElement(document.querySelector(".back_image"), "button", { textContent: "התחבר ל-" + user2[0] }) b.addEventListener("click", async function() { await login(...user2) }); } }); async function login(vb_login_username, vb_login_password) { const postData = { method: "POST", headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8', }, body: new URLSearchParams({ securitytoken: "guest", vb_login_username, vb_login_password, cookieuser: 1, do: "login" }) }; try { const data = await fetcher("https://www.fxp.co.il/login.php", postData); if (data.includes("התחברת בהצלחה")) alert("✅ Logged in successfully"); else if (data.includes("במספר הפעמים המרבי")) alert("⚠️ Login restricted"); alert("❌ Login failed: Invalid credentials"); } catch (err) { alert("Login request failed"); } }