# מניעת קריסה בלחיצה על קישור חיצוני מתוך תוסף

## הבעיה

תוספים שמציגים תוכן HTML חופשי בתוך `iframe` (למשל תוכן שמגיע מקובץ ZIM, מ-Markdown שהומר ל-HTML, מ-RSS וכו') חשופים למצב שבו התוכן מכיל קישורי `<a href="https://...">` לאתרים חיצוניים. כשמשתמש לוחץ על קישור כזה בתוך ה-`iframe`, הדפדפן/ה-WebView מנסה לנווט אליו — ובתוך סביבת ה-WebView של אוצריא (origin=null, בלי גישה לרשת חיצונית מהחלון הראשי) הניווט הזה לא "נכשל בשקט", אלא מוביל להתנהגות בלתי צפויה שקורסת את התוסף.

**דרך שגויה שנפוצה ולא עוזרת בפועל:** הוספת `target="_blank"` לקישורים חיצוניים, בתקווה ש"ה-sandbox יחסום את זה". בפועל, `target="_blank"` מפעיל נתיב קוד ברמת האפליקציה המארחת ("open new window"), ונתיב הזה הוא בדיוק מה שקורס. הפתרון הנכון הוא לא לנסות לנווט כלל — למנוע את הפעולה ברמת ה-JS ולהציג הודעה במקום.

## הפתרון

הרעיון: כשמעבדים את ה-HTML לפני הזרקתו ל-`iframe` (למשל דרך `srcdoc`), מסמנים כל קישור חיצוני בתכונת `data-*` ייעודית, **בלי** לגעת ב-`target` ובלי להסיר את ה-`href` המקורי (כדי שריחוף עם העכבר עדיין יראה את הכתובת האמיתית). ואז, ב-listener אחד ל-`click` על ה-`document` של ה-`iframe` (בשלב `capture`, כדי לתפוס את הלחיצה לפני שהדפדפן מבצע ניווט), בודקים את התכונה הזו ואם היא קיימת — קוראים ל-`preventDefault()` ומציגים הודעה.

### שלב 1 — סימון קישורים חיצוניים בעת עיבוד ה-HTML

```js
doc.querySelectorAll('a[href]').forEach((el) => {
  const href = el.getAttribute('href');
  if (!href) return;
  if (href.startsWith('#')) return; // קישור עוגן בתוך הדף — להשאיר כמו שהוא

  // isInternalLink(href) הוא פונקציה משלכם שבודקת אם הקישור מפנה
  // למשאב בתוך התוסף/הארכיון (למשל דרך new URL(href, base) ובדיקת ה-host)
  if (!isInternalLink(href)) {
    el.setAttribute('data-external-link', '1');
    return;
  }

  // כאן ממשיכים בטיפול הרגיל בקישורים פנימיים...
});
```

### שלב 2 — חסימת הלחיצה + הצגת הודעה

```js
iframe.contentDocument.addEventListener('click', (ev) => {
  let target = ev.target;
  while (target && target.tagName !== 'A') target = target.parentNode;
  if (!target) return;

  if (target.hasAttribute('data-external-link')) {
    ev.preventDefault();
    // כאן קוראים לפונקציית ההודעה של ה-SDK, למשל:
    Otzaria.call('ui.showError', { message: 'קישור זה מפנה לאתר חיצוני ואינו נגיש מתוך התוסף.' });
    return;
  }

  // טיפול בקישורים פנימיים (כרגיל)...
}, true); // true = capture — קריטי, כדי לתפוס את הלחיצה לפני הניווט הדפדפני
```

זהו. אין צורך ב-`sandbox` מיוחד ב-`iframe`, אין צורך להסיר `href`, ואין צורך ב-`target="_blank"` בכלל — ה-`preventDefault()` ב-listener שרץ ב-capture חוסם את הניווט לחלוטין, בלי תלות בערך של `target`.

## הערה נלווית: פענוח קישורים עם תווים שמורים (`:`, `/` וכו')

אם אתם ממירים קישורים יחסיים לנתיב פנימי (למשל כדי להשוות למפתח בארכיון או ברשימת דפים), שימו לב **לא** להשתמש ב-`decodeURI()` לצורך זה — היא במתכוון *לא* מפענחת תווים שמורים כמו `:` ו-`/` (הם נשארים כ-`%3A`, `%2F` וכו'), כי היא נועדה לפענוח URI מלא ולא של מזהה בודד. אם כותרת התוכן מכילה נקודתיים (נפוץ מאוד בתוכן מבוסס-MediaWiki, למשל "פרשני:עמוד ראשי" או "קטגוריה:..."), הקישור לא יתאים לנתיב האמיתי והחיפוש יחזיר "לא נמצא". הפתרון: להשתמש ב-`decodeURIComponent()` במקום, שמפענחת את כל התווים.

```js
// שגוי — ':' יישאר '%3A' ולא יתאים לנתיב האמיתי
const path = decodeURI(url.pathname.replace(/^\//, ''));

// נכון
const path = decodeURIComponent(url.pathname.replace(/^\//, ''));
```
