mirror of
https://github.com/hedgedoc/hedgedoc.git
synced 2025-05-19 17:55:17 -04:00
Wrap markdown rendering in iframe (#837)
Signed-off-by: Tilman Vatteroth <tilman.vatteroth@tu-dortmund.de>
This commit is contained in:
parent
bd31076928
commit
586969f368
45 changed files with 1014 additions and 287 deletions
|
@ -1,24 +0,0 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { DomElement } from 'domhandler'
|
||||
import { ReactElement } from 'react'
|
||||
import { ComponentReplacer, SubNodeTransform } from '../ComponentReplacer'
|
||||
|
||||
export class LinkInNewTabReplacer extends ComponentReplacer {
|
||||
public getReplacement (node: DomElement, subNodeTransform: SubNodeTransform): (ReactElement | null | undefined) {
|
||||
const isJumpMark = node.attribs?.href?.substr(0, 1) === '#'
|
||||
|
||||
if (node.name !== 'a' || isJumpMark) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
return <a className={node.attribs?.class} title={node.attribs?.title} href={node.attribs?.href} rel='noopener noreferrer' target='_blank'>
|
||||
{
|
||||
node.children?.map((child, index) => subNodeTransform(child, index))
|
||||
}
|
||||
</a>
|
||||
}
|
||||
}
|
|
@ -1,8 +1,8 @@
|
|||
/*
|
||||
SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import React, { Fragment, ReactElement, useEffect, useState } from 'react'
|
||||
import ReactHtmlParser from 'react-html-parser'
|
||||
|
@ -59,7 +59,7 @@ export const HighlightedCode: React.FC<HighlightedCodeProps> = ({ code, language
|
|||
{ dom }
|
||||
</code>
|
||||
<div className={'text-right button-inside'}>
|
||||
<CopyToClipboardButton content={code}/>
|
||||
<CopyToClipboardButton content={code} data-cy="copy-code-button"/>
|
||||
</div>
|
||||
</Fragment>)
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ import React from 'react'
|
|||
import { Modal } from 'react-bootstrap'
|
||||
import { ForkAwesomeIcon } from '../../../common/fork-awesome/fork-awesome-icon'
|
||||
import "./lightbox.scss"
|
||||
import { ProxyImageFrame } from './proxy-image-frame'
|
||||
|
||||
export interface ImageLightboxModalProps {
|
||||
show: boolean
|
||||
|
@ -33,7 +34,7 @@ export const ImageLightboxModal: React.FC<ImageLightboxModalProps> = ({ show, on
|
|||
<span>{alt ?? title ?? ''}</span>
|
||||
</Modal.Title>
|
||||
</Modal.Header>
|
||||
<img alt={alt} src={src} title={title} className={'w-100 cursor-zoom-out'} onClick={onHide}/>
|
||||
<ProxyImageFrame alt={alt} src={src} title={title} className={'w-100 cursor-zoom-out'} onClick={onHide}/>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -9,17 +9,27 @@ import React from 'react'
|
|||
import { ComponentReplacer } from '../ComponentReplacer'
|
||||
import { ProxyImageFrame } from './proxy-image-frame'
|
||||
|
||||
export type ImageClickHandler = (event: React.MouseEvent<HTMLImageElement, MouseEvent>) => void;
|
||||
|
||||
export class ImageReplacer extends ComponentReplacer {
|
||||
private readonly clickHandler?: ImageClickHandler
|
||||
|
||||
constructor (clickHandler?: ImageClickHandler) {
|
||||
super()
|
||||
this.clickHandler = clickHandler
|
||||
}
|
||||
|
||||
public getReplacement (node: DomElement): React.ReactElement | undefined {
|
||||
if (node.name === 'img' && node.attribs) {
|
||||
return <ProxyImageFrame
|
||||
id={node.attribs.id}
|
||||
className={node.attribs.class}
|
||||
className={`${node.attribs.class} cursor-zoom-in`}
|
||||
src={node.attribs.src}
|
||||
alt={node.attribs.alt}
|
||||
title={node.attribs.title}
|
||||
width={node.attribs.width}
|
||||
height={node.attribs.height}
|
||||
onClick={this.clickHandler}
|
||||
/>
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,29 +0,0 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import React, { Fragment, useState } from 'react'
|
||||
import { ImageLightboxModal } from './image-lightbox-modal'
|
||||
import "./lightbox.scss"
|
||||
|
||||
export const LightboxImageFrame: React.FC<React.ImgHTMLAttributes<HTMLImageElement>> = (
|
||||
{
|
||||
alt,
|
||||
title,
|
||||
src,
|
||||
...props
|
||||
}) => {
|
||||
const [showFullscreenImage, setShowFullscreenImage] = useState(false)
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<img alt={alt} src={src} title={title} {...props} className={'cursor-zoom-in'}
|
||||
onClick={() => setShowFullscreenImage(true)}/>
|
||||
<ImageLightboxModal
|
||||
show={showFullscreenImage}
|
||||
onHide={() => setShowFullscreenImage(false)} title={title} src={src} alt={alt}/>
|
||||
</Fragment>
|
||||
)
|
||||
}
|
|
@ -8,7 +8,6 @@ import React, { useEffect, useState } from 'react'
|
|||
import { useSelector } from 'react-redux'
|
||||
import { getProxiedUrl } from '../../../../api/media'
|
||||
import { ApplicationState } from '../../../../redux'
|
||||
import { LightboxImageFrame } from './lightbox-image-frame'
|
||||
|
||||
export const ProxyImageFrame: React.FC<React.ImgHTMLAttributes<HTMLImageElement>> = (
|
||||
{
|
||||
|
@ -29,13 +28,6 @@ export const ProxyImageFrame: React.FC<React.ImgHTMLAttributes<HTMLImageElement>
|
|||
.catch(err => console.error(err))
|
||||
}, [imageProxyEnabled, src])
|
||||
|
||||
if (imageProxyEnabled) {
|
||||
return (
|
||||
<LightboxImageFrame src={imageUrl} title={title ?? alt ?? ''} alt={alt} {...props}/>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<LightboxImageFrame src={src ?? ''} title={title ?? alt ?? ''} alt={alt} {...props}/>
|
||||
)
|
||||
return <img src={imageProxyEnabled ? imageUrl : (src ?? '')} title={title ?? alt ?? ''} alt={alt} {...props}/>
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { DomElement } from 'domhandler'
|
||||
import React, { ReactElement } from 'react'
|
||||
import { ComponentReplacer, NativeRenderer, SubNodeTransform } from '../ComponentReplacer'
|
||||
|
||||
export const createJumpToMarkClickEventHandler = (id: string) => {
|
||||
return (event: React.MouseEvent<HTMLElement, MouseEvent>): void => {
|
||||
document.getElementById(id)?.scrollIntoView()
|
||||
event.preventDefault()
|
||||
}
|
||||
}
|
||||
|
||||
export class LinkReplacer extends ComponentReplacer {
|
||||
constructor (private baseUrl?: string) {
|
||||
super()
|
||||
}
|
||||
|
||||
public getReplacement (node: DomElement, subNodeTransform: SubNodeTransform, nativeRenderer: NativeRenderer): (ReactElement | null | undefined) {
|
||||
if (node.name !== 'a' || !node.attribs || !node.attribs.href) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
const url = node.attribs.href
|
||||
const isJumpMark = url.substr(0, 1) === '#'
|
||||
|
||||
const id = url.substr(1)
|
||||
|
||||
try {
|
||||
node.attribs.href = new URL(url, this.baseUrl).toString()
|
||||
} catch (e) {
|
||||
node.attribs.href = url
|
||||
}
|
||||
|
||||
if (isJumpMark) {
|
||||
return <span onClick={createJumpToMarkClickEventHandler(id)}>
|
||||
{nativeRenderer()}
|
||||
</span>
|
||||
} else {
|
||||
node.attribs.rel = "noreferer noopener"
|
||||
node.attribs.target = "_blank"
|
||||
return nativeRenderer()
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue