mirror of
https://github.com/BookStackApp/BookStack.git
synced 2024-10-01 01:36:00 -04:00
Replaced markdown preview display iframe with div
No longer need to use the iframe sandboxing techniques, since we now have CSP rules in-place. Means that embed tags can load without CSP complications. Causes slight change to contents of `editor-markdown::setup` editor event data, since we're changing the `displayEl` property. Updates markdown editor component to make better use of the component system.
This commit is contained in:
parent
641a26cdf7
commit
1d1186c901
@ -18,10 +18,8 @@ class MarkdownEditor {
|
|||||||
this.markdown = new MarkdownIt({html: true});
|
this.markdown = new MarkdownIt({html: true});
|
||||||
this.markdown.use(mdTasksLists, {label: true});
|
this.markdown.use(mdTasksLists, {label: true});
|
||||||
|
|
||||||
this.display = this.elem.querySelector('.markdown-display');
|
this.display = this.$refs.display;
|
||||||
|
this.input = this.$refs.input;
|
||||||
this.displayStylesLoaded = false;
|
|
||||||
this.input = this.elem.querySelector('textarea');
|
|
||||||
|
|
||||||
this.cm = null;
|
this.cm = null;
|
||||||
this.Code = null;
|
this.Code = null;
|
||||||
@ -32,23 +30,13 @@ class MarkdownEditor {
|
|||||||
});
|
});
|
||||||
|
|
||||||
this.onMarkdownScroll = this.onMarkdownScroll.bind(this);
|
this.onMarkdownScroll = this.onMarkdownScroll.bind(this);
|
||||||
|
|
||||||
const displayLoad = () => {
|
|
||||||
this.displayDoc = this.display.contentDocument;
|
|
||||||
this.init(cmLoadPromise);
|
|
||||||
};
|
|
||||||
|
|
||||||
if (this.display.contentDocument.readyState === 'complete') {
|
|
||||||
displayLoad();
|
|
||||||
} else {
|
|
||||||
this.display.addEventListener('load', displayLoad.bind(this));
|
|
||||||
}
|
|
||||||
|
|
||||||
window.$events.emitPublic(this.elem, 'editor-markdown::setup', {
|
window.$events.emitPublic(this.elem, 'editor-markdown::setup', {
|
||||||
markdownIt: this.markdown,
|
markdownIt: this.markdown,
|
||||||
displayEl: this.display,
|
displayEl: this.display,
|
||||||
codeMirrorInstance: this.cm,
|
codeMirrorInstance: this.cm,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.init(cmLoadPromise);
|
||||||
}
|
}
|
||||||
|
|
||||||
init(cmLoadPromise) {
|
init(cmLoadPromise) {
|
||||||
@ -56,17 +44,17 @@ class MarkdownEditor {
|
|||||||
let lastClick = 0;
|
let lastClick = 0;
|
||||||
|
|
||||||
// Prevent markdown display link click redirect
|
// Prevent markdown display link click redirect
|
||||||
this.displayDoc.addEventListener('click', event => {
|
this.display.addEventListener('click', event => {
|
||||||
let isDblClick = Date.now() - lastClick < 300;
|
const isDblClick = Date.now() - lastClick < 300;
|
||||||
|
|
||||||
let link = event.target.closest('a');
|
const link = event.target.closest('a');
|
||||||
if (link !== null) {
|
if (link !== null) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
window.open(link.getAttribute('href'));
|
window.open(link.getAttribute('href'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let drawing = event.target.closest('[drawio-diagram]');
|
const drawing = event.target.closest('[drawio-diagram]');
|
||||||
if (drawing !== null && isDblClick) {
|
if (drawing !== null && isDblClick) {
|
||||||
this.actionEditDrawing(drawing);
|
this.actionEditDrawing(drawing);
|
||||||
return;
|
return;
|
||||||
@ -77,10 +65,10 @@ class MarkdownEditor {
|
|||||||
|
|
||||||
// Button actions
|
// Button actions
|
||||||
this.elem.addEventListener('click', event => {
|
this.elem.addEventListener('click', event => {
|
||||||
let button = event.target.closest('button[data-action]');
|
const button = event.target.closest('button[data-action]');
|
||||||
if (button === null) return;
|
if (button === null) return;
|
||||||
|
|
||||||
let action = button.getAttribute('data-action');
|
const action = button.getAttribute('data-action');
|
||||||
if (action === 'insertImage') this.actionInsertImage();
|
if (action === 'insertImage') this.actionInsertImage();
|
||||||
if (action === 'insertLink') this.actionShowLinkSelector();
|
if (action === 'insertLink') this.actionShowLinkSelector();
|
||||||
if (action === 'insertDrawing' && (event.ctrlKey || event.metaKey)) {
|
if (action === 'insertDrawing' && (event.ctrlKey || event.metaKey)) {
|
||||||
@ -132,35 +120,11 @@ class MarkdownEditor {
|
|||||||
window.$events.emit('editor-markdown-change', content);
|
window.$events.emit('editor-markdown-change', content);
|
||||||
|
|
||||||
// Set body content
|
// Set body content
|
||||||
this.displayDoc.body.className = 'page-content';
|
this.display.innerHTML = html;
|
||||||
this.displayDoc.body.innerHTML = html;
|
|
||||||
|
|
||||||
// Copy styles from page head and set custom styles for editor
|
|
||||||
this.loadStylesIntoDisplay();
|
|
||||||
}
|
|
||||||
|
|
||||||
loadStylesIntoDisplay() {
|
|
||||||
if (this.displayStylesLoaded) return;
|
|
||||||
this.displayDoc.documentElement.classList.add('markdown-editor-display');
|
|
||||||
// Set display to be dark mode if parent is
|
|
||||||
|
|
||||||
if (document.documentElement.classList.contains('dark-mode')) {
|
|
||||||
this.displayDoc.documentElement.style.backgroundColor = '#222';
|
|
||||||
this.displayDoc.documentElement.classList.add('dark-mode');
|
|
||||||
}
|
|
||||||
|
|
||||||
this.displayDoc.head.innerHTML = '';
|
|
||||||
const styles = document.head.querySelectorAll('style,link[rel=stylesheet]');
|
|
||||||
for (let style of styles) {
|
|
||||||
const copy = style.cloneNode(true);
|
|
||||||
this.displayDoc.head.appendChild(copy);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.displayStylesLoaded = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onMarkdownScroll(lineCount) {
|
onMarkdownScroll(lineCount) {
|
||||||
const elems = this.displayDoc.body.children;
|
const elems = this.display.children;
|
||||||
if (elems.length <= lineCount) return;
|
if (elems.length <= lineCount) return;
|
||||||
|
|
||||||
const topElem = (lineCount === -1) ? elems[elems.length-1] : elems[lineCount];
|
const topElem = (lineCount === -1) ? elems[elems.length-1] : elems[lineCount];
|
||||||
@ -317,7 +281,7 @@ class MarkdownEditor {
|
|||||||
let cursor = cm.getCursor();
|
let cursor = cm.getCursor();
|
||||||
let lineContent = cm.getLine(cursor.line);
|
let lineContent = cm.getLine(cursor.line);
|
||||||
let lineLen = lineContent.length;
|
let lineLen = lineContent.length;
|
||||||
let newLineContent = lineContent;
|
let newLineContent;
|
||||||
|
|
||||||
if (lineContent.indexOf(start) === 0 && lineContent.slice(-end.length) === end) {
|
if (lineContent.indexOf(start) === 0 && lineContent.slice(-end.length) === end) {
|
||||||
newLineContent = lineContent.slice(start.length, lineContent.length - end.length);
|
newLineContent = lineContent.slice(start.length, lineContent.length - end.length);
|
||||||
@ -333,9 +297,9 @@ class MarkdownEditor {
|
|||||||
let selection = cm.getSelection();
|
let selection = cm.getSelection();
|
||||||
if (selection === '') return wrapLine(start, end);
|
if (selection === '') return wrapLine(start, end);
|
||||||
|
|
||||||
let newSelection = selection;
|
let newSelection;
|
||||||
let frontDiff = 0;
|
let frontDiff = 0;
|
||||||
let endDiff = 0;
|
let endDiff;
|
||||||
|
|
||||||
if (selection.indexOf(start) === 0 && selection.slice(-end.length) === end) {
|
if (selection.indexOf(start) === 0 && selection.slice(-end.length) === end) {
|
||||||
newSelection = selection.slice(start.length, selection.length - end.length);
|
newSelection = selection.slice(start.length, selection.length - end.length);
|
||||||
|
@ -114,26 +114,20 @@
|
|||||||
|
|
||||||
.markdown-display {
|
.markdown-display {
|
||||||
margin-inline-start: -1px;
|
margin-inline-start: -1px;
|
||||||
}
|
|
||||||
|
|
||||||
.markdown-editor-display {
|
|
||||||
background-color: #fff;
|
|
||||||
body {
|
|
||||||
display: block;
|
display: block;
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
padding-inline-start: 16px;
|
padding: $-m;
|
||||||
padding-inline-end: 16px;
|
overflow-y: scroll;
|
||||||
}
|
|
||||||
[drawio-diagram]:hover {
|
[drawio-diagram]:hover {
|
||||||
outline: 2px solid var(--color-primary);
|
outline: 2px solid var(--color-primary);
|
||||||
}
|
}
|
||||||
|
[drawio-diagram] embed {
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
html.markdown-editor-display.dark-mode {
|
.dark-mode .markdown-display {
|
||||||
background-color: #222;
|
background-color: #222;
|
||||||
body {
|
|
||||||
background-color: #222;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.editor-toolbar {
|
.editor-toolbar {
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
|
|
||||||
<div markdown-input class="flex flex-fill">
|
<div markdown-input class="flex flex-fill">
|
||||||
<textarea id="markdown-editor-input"
|
<textarea id="markdown-editor-input"
|
||||||
|
refs="markdown-editor@input"
|
||||||
@if($errors->has('markdown')) class="text-neg" @endif
|
@if($errors->has('markdown')) class="text-neg" @endif
|
||||||
name="markdown"
|
name="markdown"
|
||||||
rows="5">@if(isset($model) || old('markdown')){{ old('markdown') ?? ($model->markdown === '' ? $model->html : $model->markdown) }}@endif</textarea>
|
rows="5">@if(isset($model) || old('markdown')){{ old('markdown') ?? ($model->markdown === '' ? $model->html : $model->markdown) }}@endif</textarea>
|
||||||
@ -34,7 +35,10 @@
|
|||||||
<div class="editor-toolbar">
|
<div class="editor-toolbar">
|
||||||
<div class="editor-toolbar-label">{{ trans('entities.pages_md_preview') }}</div>
|
<div class="editor-toolbar-label">{{ trans('entities.pages_md_preview') }}</div>
|
||||||
</div>
|
</div>
|
||||||
<iframe src="about:blank" class="markdown-display" sandbox="allow-same-origin"></iframe>
|
<div class="markdown-display">
|
||||||
|
<div refs="markdown-editor@display"
|
||||||
|
class="page-content"></div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user