|  | 
| 4 | 4 | ENABLED = true | 
| 5 | 5 | FILE_EXTENSIONS = .in-iframe | 
| 6 | 6 | RENDER_CONTENT_MODE = iframe | 
| 7 |  | -RENDER_COMMAND = `echo '<div style="width: 100%; height: 2000px; border: 10px solid red; box-sizing: border-box;">content</div>'` | 
|  | 7 | +RENDER_COMMAND = `echo '<div style="width: 100%; height: 2000px; border: 10px solid red; box-sizing: border-box;"><a href="/">a link</a> <a target="_blank" href="//gitea.com">external link</a></div>'` | 
| 8 | 8 | 
 | 
| 9 | 9 | */ | 
| 10 | 10 | 
 | 
| 11 | 11 | function mainExternalRenderIframe() { | 
| 12 | 12 |   const u = new URL(window.location.href); | 
| 13 |  | -  const fn = () => window.parent.postMessage({ | 
| 14 |  | -    giteaIframeCmd: 'resize', | 
| 15 |  | -    giteaIframeId: u.searchParams.get('gitea-iframe-id'), | 
| 16 |  | -    giteaIframeHeight: document.documentElement.scrollHeight, | 
| 17 |  | -  }, '*'); | 
| 18 |  | -  fn(); | 
| 19 |  | -  window.addEventListener('DOMContentLoaded', fn); | 
| 20 |  | -  setInterval(fn, 1000); | 
|  | 13 | +  const iframeId = u.searchParams.get('gitea-iframe-id'); | 
| 21 | 14 | 
 | 
| 22 |  | -  // make all absolute links open in new window (otherwise they would be blocked by all parents' frame-src) | 
| 23 |  | -  document.body.addEventListener('click', (e) => { | 
|  | 15 | +  // iframe is in different origin, so we need to use postMessage to communicate | 
|  | 16 | +  const postIframeMsg = (cmd: string, data: Record<string, any> = {}) => { | 
|  | 17 | +    window.parent.postMessage({giteaIframeCmd: cmd, giteaIframeId: iframeId, ...data}, '*'); | 
|  | 18 | +  }; | 
|  | 19 | + | 
|  | 20 | +  const updateIframeHeight = () => postIframeMsg('resize', {iframeHeight: document.documentElement.scrollHeight}); | 
|  | 21 | +  updateIframeHeight(); | 
|  | 22 | +  window.addEventListener('DOMContentLoaded', updateIframeHeight); | 
|  | 23 | +  // the easiest way to handle dynamic content changes and easy to debug, can be fine-tuned in the future | 
|  | 24 | +  setInterval(updateIframeHeight, 1000); | 
|  | 25 | + | 
|  | 26 | +  //  no way to open an absolute link with CSP frame-src, it also needs some tricks like "postMessage" or "copy the link to clipboard" | 
|  | 27 | +  const openIframeLink = (link: string, target: string) => postIframeMsg('open-link', {openLink: link, anchorTarget: target}); | 
|  | 28 | +  document.addEventListener('click', (e) => { | 
| 24 | 29 |     const el = e.target as HTMLAnchorElement; | 
| 25 | 30 |     if (el.nodeName !== 'A') return; | 
| 26 |  | -    const href = el.getAttribute('href'); | 
| 27 |  | -    if (!href.startsWith('//') && !href.includes('://')) return; | 
| 28 |  | -    el.target = '_blank'; | 
| 29 |  | -  }, true); | 
|  | 31 | +    const href = el.getAttribute('href') || ''; | 
|  | 32 | +    // safe links: "./any", "../any", "/any", "//host/any", "http://host/any", "https://host/any" | 
|  | 33 | +    if (href.startsWith('.') || href.startsWith('/') || href.startsWith('http://') || href.startsWith('https://')) { | 
|  | 34 | +      e.preventDefault(); | 
|  | 35 | +      openIframeLink(href, el.getAttribute('target')); | 
|  | 36 | +    } | 
|  | 37 | +  }); | 
| 30 | 38 | } | 
| 31 | 39 | 
 | 
| 32 | 40 | mainExternalRenderIframe(); | 
0 commit comments