@אז-נדברו כתב:
כיון ששמואל לכאורה לא יקדיש זמן לטפל בזה יצרתי לך סקריפט בtampermonkey לתיקון כפתורי "חיפוש גוגל בפורום" ו"חיפוש בהגדרות מתקדמות" (כאן יש הוראות התקנה לתוסף).
הנה הסקריפט:
// ==UserScript==
// @name פורום ימות - תיקון כפתורי החיפוש
// @namespace https://f2.freeivr.co.il/
// @version 1.0.0
// @description מתקן את "חיפוש גוגל בפורום" ואת "חיפוש בהגדרות המתקדמות" - שניהם פותחים חלון חיפוש
// @match https://f2.freeivr.co.il/*
// @run-at document-idle
// @grant none
// ==/UserScript==
(function () {
'use strict';
const LOGO_URL = 'https://f2.freeivr.co.il/assets/uploads/system/site-logo.gif?v=090613ffd44';
const SITE = location.hostname; // f2.freeivr.co.il
const MODAL_ID = 'ym-search-modal';
const ADV_OLD_LABEL = 'חיפוש בהגדרות המתקדמות';
const ADV_LABEL = 'חיפוש באזור התעשייה - הגדרות המערכת';
// ================= עזרים =================
function matchesNavButton(el, hrefRegex, title) {
const a = el.closest('a');
if (!a) return false;
if (hrefRegex.test(a.getAttribute('href') || '')) return true;
const li = a.closest('li');
return !!(li && (li.getAttribute('data-bs-original-title') === title ||
li.getAttribute('title') === title));
}
function currentTopicId() {
const m = location.pathname.match(/\/topic\/(\d+)/);
return m ? m[1] : null;
}
// ================= חיפוש גוגל =================
function googleSearch(query, topicId) {
const scope = topicId ? `site:${SITE}/topic/${topicId}/` : `site:${SITE}`;
const url = 'https://www.google.com/search?q=' + encodeURIComponent(`${scope} ${query}`);
window.open(url, '_blank', 'noopener');
}
function openGoogleModal() {
const topicId = currentTopicId();
openSearchModal({
placeholder: 'הקלידו כדי לחפש בגוגל',
logoAlt: 'חיפוש בגוגל',
buttons: [
{
label: 'חיפוש בכל הפורום',
cls: 'btn-secondary',
title: 'לחצו על אנטר כדי לחפש',
isEnter: true,
run: (q) => googleSearch(q, null),
},
{
label: 'חיפוש בנושא זה',
cls: 'btn-primary',
available: !!topicId,
unavailableTitle: 'זמין רק בתוך נושא',
run: (q) => googleSearch(q, topicId),
},
],
});
}
// ================= חיפוש בהגדרות המתקדמות =================
function advancedSearchPath(term) {
return 'search?in=titlesposts&term=' + encodeURIComponent(term) +
'&matchWords=all&by=&categories%5B%5D=1&searchChildren=true&hasTags=&replies=' +
'&repliesFilter=atleast&timeFilter=newer&timeRange=&sortBy=relevance' +
'&sortDirection=desc&showAs=posts';
}
function goAdvancedSearch(term, newTab) {
const path = advancedSearchPath(term);
if (newTab) {
window.open('/' + path, '_blank', 'noopener');
} else if (window.ajaxify && typeof window.ajaxify.go === 'function') {
window.ajaxify.go(path); // ניווט SPA בלי רענון
} else {
location.href = '/' + path;
}
}
function openAdvancedModal() {
openSearchModal({
placeholder: ADV_LABEL,
logoAlt: 'חיפוש בפורום',
buttons: [
{
label: 'חיפוש',
cls: 'btn-primary',
title: 'אנטר לחיפוש, Ctrl+אנטר או Ctrl+לחיצה לפתיחה בלשונית חדשה',
isEnter: true,
run: (q, newTab) => goAdvancedSearch(q, newTab),
},
],
});
}
// ================= יירוט הלחיצות =================
document.addEventListener('click', (e) => {
let handler = null;
if (matchesNavButton(e.target, /\/misc\/freeivr\/?$/, 'חיפוש גוגל בפורום')) {
handler = openGoogleModal;
} else if (matchesNavButton(e.target, /^\/?SearchSettings\/?$/i, ADV_OLD_LABEL) ||
matchesNavButton(e.target, /^\/?SearchSettings\/?$/i, ADV_LABEL)) {
handler = openAdvancedModal;
}
if (!handler) return;
e.preventDefault();
e.stopImmediatePropagation(); // מונע את ajaxify של NodeBB
handler();
}, true);
// ================= שינוי התווית של כפתור ההגדרות המתקדמות =================
function relabelAdvancedButton() {
document.querySelectorAll('a[href="/SearchSettings"], a[href="SearchSettings"]').forEach((a) => {
const li = a.closest('li');
if (!li || li.dataset.ymRelabeled === '1') return;
li.dataset.ymRelabeled = '1';
// הטקסט הצף (Bootstrap tooltip)
li.setAttribute('data-bs-original-title', ADV_LABEL);
if (li.hasAttribute('title')) li.setAttribute('title', ADV_LABEL);
a.setAttribute('aria-label', ADV_LABEL);
try {
const tip = window.bootstrap && window.bootstrap.Tooltip &&
window.bootstrap.Tooltip.getInstance(li);
if (tip && typeof tip.setContent === 'function') {
tip.setContent({ '.tooltip-inner': ADV_LABEL });
}
} catch (err) { /* לא קריטי */ }
// הטקסט שמוצג בתפריט בנייד
const span = a.querySelector('span.visible-xs-inline');
if (span) span.textContent = ADV_LABEL;
});
}
relabelAdvancedButton();
let relabelPending = false;
new MutationObserver(() => {
if (relabelPending) return;
relabelPending = true;
requestAnimationFrame(() => { relabelPending = false; relabelAdvancedButton(); });
}).observe(document.body, { childList: true, subtree: true });
// ================= סגנון =================
function injectStyle() {
if (document.getElementById(MODAL_ID + '-style')) return;
const css = `
#${MODAL_ID}{position:fixed;inset:0;z-index:2000;display:flex;align-items:flex-start;justify-content:center;
padding:1.75rem .5rem;background:rgba(0,0,0,.5);direction:rtl;}
#${MODAL_ID} .ymg-dialog{background:var(--bs-body-bg,#fff);color:var(--bs-body-color,#212529);
width:100%;max-width:500px;border-radius:.5rem;box-shadow:0 .5rem 1rem rgba(0,0,0,.25);overflow:hidden;}
#${MODAL_ID} .ymg-header{display:flex;align-items:center;justify-content:space-between;gap:.5rem;
padding:1rem;border-bottom:1px solid var(--bs-border-color,#dee2e6);}
#${MODAL_ID} .ymg-logo{flex:1;text-align:center;}
#${MODAL_ID} .ymg-logo img{max-width:100%;max-height:90px;}
#${MODAL_ID} .ymg-close{background:none;border:0;font-size:1.75rem;line-height:1;cursor:pointer;
color:inherit;opacity:.6;padding:0 .25rem;}
#${MODAL_ID} .ymg-close:hover{opacity:1;}
#${MODAL_ID} .ymg-body{padding:1rem;}
#${MODAL_ID} .ymg-footer{display:flex;flex-wrap:wrap;gap:.5rem;justify-content:flex-start;
padding:.75rem 1rem;border-top:1px solid var(--bs-border-color,#dee2e6);}
#${MODAL_ID} .btn[disabled]{opacity:.5;cursor:not-allowed;}
`;
const style = document.createElement('style');
style.id = MODAL_ID + '-style';
style.textContent = css;
document.head.appendChild(style);
}
// ================= חלון חיפוש כללי =================
// buttons: [{ label, cls, title?, isEnter?, available? (ברירת מחדל true), unavailableTitle?, run(query, newTab) }]
function openSearchModal({ placeholder, logoAlt, buttons }) {
if (document.getElementById(MODAL_ID)) return;
injectStyle();
const wrap = document.createElement('div');
wrap.id = MODAL_ID;
wrap.setAttribute('role', 'dialog');
wrap.setAttribute('aria-modal', 'true');
wrap.innerHTML = `
<div class="ymg-dialog">
<div class="ymg-header">
<div class="ymg-logo"><img alt=""></div>
<button type="button" class="ymg-close" aria-label="סגירה">×</button>
</div>
<div class="ymg-body">
<input type="text" class="form-control ymg-input" autocomplete="off">
</div>
<div class="ymg-footer"></div>
</div>`;
const img = wrap.querySelector('.ymg-logo img');
img.src = LOGO_URL;
img.alt = logoAlt;
const input = wrap.querySelector('.ymg-input');
input.placeholder = placeholder;
const footer = wrap.querySelector('.ymg-footer');
const btnEls = buttons.map((b) => {
const el = document.createElement('button');
el.type = 'button';
el.className = 'btn ' + b.cls;
el.textContent = b.label;
el.disabled = true;
const available = b.available !== false;
const t = available ? b.title : b.unavailableTitle;
if (t) el.title = t;
el.addEventListener('click', (e) => run(b, e.ctrlKey || e.metaKey));
footer.appendChild(el);
return { def: b, el, available };
});
const cancel = document.createElement('button');
cancel.type = 'button';
cancel.className = 'btn btn-light';
cancel.textContent = 'ביטול';
footer.appendChild(cancel);
document.body.appendChild(wrap);
const query = () => input.value.trim();
const updateButtons = () => {
const hasText = query().length > 0;
btnEls.forEach(({ el, available }) => { el.disabled = !hasText || !available; });
};
const close = () => {
wrap.remove();
document.removeEventListener('keydown', onKey, true);
};
function run(b, newTab) {
const q = query();
if (!q) { input.focus(); return; }
close();
b.run(q, newTab);
}
const onKey = (e) => {
if (e.key === 'Escape') {
e.preventDefault();
close();
} else if (e.key === 'Enter' && document.activeElement === input) {
e.preventDefault();
const enterBtn = btnEls.find(({ def }) => def.isEnter);
if (enterBtn && !enterBtn.el.disabled) run(enterBtn.def, e.ctrlKey || e.metaKey);
}
};
input.addEventListener('input', updateButtons);
wrap.querySelector('.ymg-close').addEventListener('click', close);
cancel.addEventListener('click', close);
wrap.addEventListener('mousedown', (e) => { if (e.target === wrap) close(); });
document.addEventListener('keydown', onKey, true);
setTimeout(() => input.focus(), 0);
}
})();