This commit is contained in:
caishi 2020-12-11 18:14:25 +08:00
parent 851afbc6c4
commit e95ef11058
6 changed files with 62 additions and 41 deletions

5
package-lock.json generated
View File

@ -4962,6 +4962,11 @@
"domelementtype": "1"
}
},
"dompurify": {
"version": "2.0.15",
"resolved": "https://registry.npm.taobao.org/dompurify/download/dompurify-2.0.15.tgz?cache=0&sync_timestamp=1607352578938&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fdompurify%2Fdownload%2Fdompurify-2.0.15.tgz",
"integrity": "sha1-gOMA/D6JVHvQrxr/LrqIzhf8neo="
},
"domutils": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz",

View File

@ -26,6 +26,7 @@
"codemirror": "^5.53.0",
"connected-react-router": "4.4.1",
"css-loader": "^3.5.2",
"dompurify": "^2.0.15",
"dotenv": "4.0.0",
"dotenv-expand": "4.2.0",
"echarts": "^4.7.0",

View File

@ -48,7 +48,7 @@ function buildToc(coll, k, level, ctx) {
});
ctx.push("</ul>")
}
ctx.push("</li>")
ctx.push("</li>");
k = buildToc(coll, k, level, ctx)
return k
}
@ -157,7 +157,6 @@ renderer.heading = function (text, level, raw) {
})
return '<h' + level + ' id="' + anchor + '">' + text + '</h' + level + '>'
}
marked.setOptions({
silent: true,
smartypants: true,

View File

@ -1,37 +1,44 @@
import React, { useEffect, useRef, useMemo } from "react";
import "katex/dist/katex.min.css";
import { renderToString } from 'katex';
import marked, { getTocContent, cleanToc, getMathExpressions, resetMathExpressions } from "../common/marked";
import React, { useEffect, useRef, useMemo } from 'react'
import 'katex/dist/katex.min.css'
import marked, { getTocContent, cleanToc, getMathExpressions, resetMathExpressions } from '../common/marked';
import 'code-prettify'
import dompurify from 'dompurify'
import { renderToString } from 'katex'
const preRegex = /<pre[^>]*>/g
function _unescape(str) {
let div = document.createElement('div')
div.innerHTML = str
return div.childNodes.length === 0 ? "" : div.childNodes[0].nodeValue;
return div.childNodes.length === 0 ? "" : div.childNodes[0].nodeValue
}
export default ({ value = '', className, style = {} }) => {
export default ({
value = '',
className,
style = {},
}) => {
let str = String(value)
const html = useMemo(() => {
let rs = marked(str)
const math_expressions = getMathExpressions()
const math_expressions = getMathExpressions();
if (str.match(/\[TOC\]/)) {
rs = rs.replace("<p>[TOC]</p>", getTocContent())
cleanToc()
}
rs = rs.replace(/(__special_katext_id_\d+__)/g, (_match, capture) => {
const { type, expression } = math_expressions[capture]
return renderToString(_unescape(expression), { displayMode: type === 'block', throwOnError: false, output: 'html' })
return renderToString(_unescape(expression) || '', { displayMode: type === 'block', throwOnError: false, output: 'html' })
})
rs = rs.replace(/▁/g, "▁▁▁")
resetMathExpressions()
return rs
return dompurify.sanitize(rs)
}, [str])
const el = useRef()
const el = useRef();
function onAncherHandler(e) {
let target = e.target
if (target.tagName.toUpperCase() === 'A') {
@ -40,7 +47,7 @@ export default ({ value = '', className, style = {} }) => {
e.preventDefault()
let viewEl = document.getElementById(ancher.replace('#', ''))
if (viewEl) {
viewEl.parentNode.scrollTop = viewEl.offsetTop
viewEl.scrollIntoView(true)
}
}
}
@ -61,6 +68,12 @@ export default ({ value = '', className, style = {} }) => {
}
}
}, [html, el.current, onAncherHandler])
return (<div ref={el} style={style} className={`${className ? className : ''} markdown-body`} dangerouslySetInnerHTML={{ __html: html }}></div>)
return (
<div
ref={el}
style={style}
className={`${className ? className : ''} markdown-body`}
dangerouslySetInnerHTML={{ __html: html }}
></div>
)
}

View File

@ -212,7 +212,10 @@ class order extends Component {
<Menu.Item
key={item.id}
onClick={(e) => this.getOption(e, id, item.name, toGet)}
style={{textAlign:item.color ? "left" :"center"}}
>
{/* 标签前面的颜色tag */}
{item.color && <span className="tagColor" style={{backgroundColor:`${item.color}`}}></span>}
{item.name}
</Menu.Item>
);

View File

@ -1,16 +1,16 @@
import React, { Fragment, useEffect, useRef, useState } from 'react'
import { getUploadActionUrl, getUrl } from 'educoder'
import React, { Fragment, useEffect, useRef, useState } from 'react';
import { getUploadActionUrl, getUrl } from 'educoder';
import ResizeObserver from 'resize-observer-polyfill';
import '../../courses/css/Courses.css'
import './css/TPMchallengesnew.css'
import 'codemirror/lib/codemirror.css'
import '../../courses/css/Courses.css';
import './css/TPMchallengesnew.css';
import 'codemirror/lib/codemirror.css';
const $ = window.$
const mdIcons = ["bold", "italic", "|", "list-ul", "list-ol", "|", "code", "code-block", "link", "|", "inline-latex", "latex", '|', "image", "table", '|', "line-break", "watch", "clear"]
const mdIcons = ["bold", "italic", "|", "list-ul", "list-ol", "|", "code", "code-block", "link", "|", "inline-latex", "latex", '|', "image", "table", '|', "line-break", "watch", "clear"];
const NULL_CH = '▁'
const NULL_CH = '▁';
function md_add_data(k, mdu, d) {
window.sessionStorage.setItem(k + mdu, d);
@ -42,7 +42,7 @@ window.md_rec_data = md_rec_data;
function md_elocalStorage(editor, mdu, id) {
let oc = window.sessionStorage.getItem('content' + mdu)
if (oc !== null && oc != editor.getValue()) {
if (oc !== null && oc !== editor.getValue()) {
$("#e_tips_" + id).data('editor', editor);
let h = '您上次有已保存的数据,是否<a style="cursor: pointer;" class="link-color-blue" onclick="md_rec_data(\'content\',\'' + mdu + '\',\'' + id + '\')">恢复</a> ? / <a style="cursor: pointer;" class="link-color-blue" onclick="md_clear_data(\'content\',\'' + mdu + '\',\'' + id + '\')">不恢复</a>';
$("#e_tips_" + id).html(h)
@ -56,15 +56,15 @@ function md_elocalStorage(editor, mdu, id) {
m = m < 10 ? '0' + m : m;
s = s < 10 ? '0' + s : s;
if (editor.getValue().trim() != "") {
if (editor.getValue().trim() !== "") {
md_add_data("content", mdu, editor.getValue());
let id2 = "#e_tips_" + id;
let textStart = " 数据已于 "
let textStart = " 数据已于 ";
let text = textStart + h + ':' + m + ':' + s + " 保存 ";
// 占位符
let oldHtml = $(id2).html();
if (oldHtml && oldHtml != ' ' && oldHtml.startsWith(textStart) == false) {
if (oldHtml && oldHtml !== ' ' && oldHtml.startsWith(textStart) === false) {
$(id2).html(oldHtml.split(' (')[0] + ` (${text})`);
} else {
$(id2).html(text);
@ -77,28 +77,28 @@ function md_elocalStorage(editor, mdu, id) {
export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, className = '', noStorage = false, imageExpand = true, placeholder = '', width = '100%', height = 400, initValue = '', emoji, watch, showNullButton = false, showResizeBar = false, startInit = true }) => {
const editorEl = useRef()
const resizeBarEl = useRef()
const [editorInstance, setEditorInstance] = useState()
const containerId = `mdEditor_${mdID}`
const editorBodyId = `mdEditors_${mdID}`
const tipId = `e_tips_mdEditor_${mdID}`
const editorEl = useRef();
const resizeBarEl = useRef();
const [editorInstance, setEditorInstance] = useState();
const containerId = `mdEditor_${mdID}`;
const editorBodyId = `mdEditors_${mdID}`;
const tipId = `e_tips_mdEditor_${mdID}`;
function onLayout() {
let ro
let ro;
if (editorEl.current) {
ro = new ResizeObserver(entries => {
for (let entry of entries) {
if (entry.target.offsetHeight > 0 || entry.target.offsetWidth > 0) {
editorInstance.resize()
editorInstance.cm.refresh()
editorInstance.cm.focus()
editorInstance.resize();
editorInstance.cm.refresh();
editorInstance.cm.focus();
}
}
})
ro.observe(editorEl.current)
ro.observe(editorEl.current);
}
return ro
return ro;
}
useEffect(() => {
@ -211,7 +211,7 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla
useEffect(() => {
if (editorInstance && initValue !== undefined) {
if (initValue != null && initValue != editorInstance.getValue()) {
if (initValue !== null && initValue !== editorInstance.getValue()) {
editorInstance.setValue(initValue.toString())
}
}
@ -265,7 +265,7 @@ export default ({ mdID, onChange, onCMBeforeChange, onCMBlur, error = false, cla
</div>
{showResizeBar ? <a ref={resizeBarEl} className='editor-resize'></a> : null}
<div className={"fr rememberTip"}>
{noStorage == true ? null : <div id={tipId} className="edu-txt-right color-grey-cd font-12"></div>}
{noStorage === true ? null : <div id={tipId} className="edu-txt-right color-grey-cd font-12"></div>}
</div>
</Fragment>
)