2019-01-15 05:22:00 +00:00
|
|
|
import React from 'react';
|
|
|
|
import PropTypes from 'prop-types';
|
2019-01-29 03:03:43 +00:00
|
|
|
import { repoID, slug, serviceURL, isPublicWiki } from '../utils/constants';
|
|
|
|
import { Utils } from '../utils/utils';
|
2019-04-28 07:38:21 +00:00
|
|
|
import { Value } from 'slate';
|
|
|
|
import { deserialize } from '@seafile/seafile-editor/dist/utils/slate2markdown';
|
|
|
|
import'../css/index-viewer.css';
|
2019-01-15 05:22:00 +00:00
|
|
|
|
|
|
|
const viewerPropTypes = {
|
2019-01-28 08:48:03 +00:00
|
|
|
indexContent: PropTypes.string.isRequired,
|
|
|
|
onLinkClick: PropTypes.func.isRequired,
|
2019-01-15 05:22:00 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
class IndexContentViewer extends React.Component {
|
|
|
|
|
2019-01-29 03:03:43 +00:00
|
|
|
constructor(props) {
|
|
|
|
super(props);
|
|
|
|
this.links = [];
|
2019-04-28 07:38:21 +00:00
|
|
|
this.rootNode = {};
|
|
|
|
}
|
|
|
|
|
|
|
|
componentWillMount() {
|
|
|
|
this.getRootNode();
|
2019-01-29 03:03:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
componentDidMount() {
|
2019-04-28 07:38:21 +00:00
|
|
|
this.bindClickEvent();
|
2019-01-29 03:03:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
componentWillReceiveProps() {
|
2019-04-28 07:38:21 +00:00
|
|
|
this.removeClickEvent();
|
2019-01-29 03:03:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
componentDidUpdate() {
|
2019-04-28 07:38:21 +00:00
|
|
|
this.bindClickEvent();
|
|
|
|
}
|
|
|
|
|
|
|
|
componentWillUnmount() {
|
|
|
|
this.removeClickEvent();
|
|
|
|
}
|
|
|
|
|
|
|
|
bindClickEvent = () => {
|
|
|
|
const contentClass = 'wiki-nav-content';
|
2019-01-29 03:03:43 +00:00
|
|
|
this.links = document.querySelectorAll(`.${contentClass} a`);
|
|
|
|
this.links.forEach(link => {
|
|
|
|
link.addEventListener('click', this.onLinkClick);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2019-04-28 07:38:21 +00:00
|
|
|
removeClickEvent = () => {
|
2019-01-29 03:03:43 +00:00
|
|
|
this.links.forEach(link => {
|
|
|
|
link.removeEventListener('click', this.onLinkClick);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2019-01-15 05:22:00 +00:00
|
|
|
onLinkClick = (event) => {
|
2019-01-29 03:03:43 +00:00
|
|
|
event.preventDefault();
|
|
|
|
event.stopPropagation();
|
2019-04-28 07:38:21 +00:00
|
|
|
const link = this.getLink(event.target);
|
|
|
|
if (link) this.props.onLinkClick(link);
|
|
|
|
}
|
2019-04-24 08:40:23 +00:00
|
|
|
|
2019-04-28 07:38:21 +00:00
|
|
|
getLink = (node) => {
|
|
|
|
const tagName = node.tagName;
|
|
|
|
if (!tagName || tagName === 'HTML') return;
|
|
|
|
if (tagName === 'A') {
|
|
|
|
return node.href;
|
2019-01-29 03:03:43 +00:00
|
|
|
} else {
|
2019-04-28 07:38:21 +00:00
|
|
|
return this.getLink(node.parentNode);
|
2019-01-29 03:03:43 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
changeInlineNode = (item) => {
|
|
|
|
if (item.object == 'inline') {
|
|
|
|
let url;
|
|
|
|
|
|
|
|
// change image url
|
|
|
|
if (item.type == 'image' && isPublicWiki) {
|
|
|
|
url = item.data.src;
|
|
|
|
const re = new RegExp(serviceURL + '/lib/' + repoID +'/file.*raw=1');
|
|
|
|
// different repo
|
|
|
|
if (!re.test(url)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// get image path
|
|
|
|
let index = url.indexOf('/file');
|
|
|
|
let index2 = url.indexOf('?');
|
|
|
|
const imagePath = url.substring(index + 5, index2);
|
|
|
|
// replace url
|
|
|
|
item.data.src = serviceURL + '/view-image-via-public-wiki/?slug=' + slug + '&path=' + imagePath;
|
|
|
|
}
|
|
|
|
|
|
|
|
else if (item.type == 'link') {
|
|
|
|
url = item.data.href;
|
2019-04-24 08:40:23 +00:00
|
|
|
|
2019-04-28 07:38:21 +00:00
|
|
|
/* eslint-disable */
|
2019-04-24 08:40:23 +00:00
|
|
|
let expression = /((([A-Za-z]{3,9}:(?:\/\/)?)(?:[\-;:&=\+\$,\w]+@)?[A-Za-z0-9\.\-]+|(?:www\.|[\-;:&=\+\$,\w]+@)[A-Za-z0-9\.\-]+)((?:\/[\+~%\/\.\w\-_]*)?\??(?:[\-\+=&;%@\.\w_]*)#?(?:[\.\!\/\\\w]*))?)/
|
2019-04-28 07:38:21 +00:00
|
|
|
/* eslint-enable */
|
2019-04-24 08:40:23 +00:00
|
|
|
let re = new RegExp(expression);
|
|
|
|
|
|
|
|
// Solving relative paths
|
|
|
|
if (!re.test(url)) {
|
2019-04-28 07:38:21 +00:00
|
|
|
item.data.href = serviceURL + '/published/' + slug + '/' + url;
|
2019-04-24 08:40:23 +00:00
|
|
|
}
|
2019-01-29 03:03:43 +00:00
|
|
|
// change file url
|
2019-04-24 08:40:23 +00:00
|
|
|
else if (Utils.isInternalMarkdownLink(url, repoID)) {
|
2019-01-29 03:03:43 +00:00
|
|
|
let path = Utils.getPathFromInternalMarkdownLink(url, repoID);
|
|
|
|
// replace url
|
2019-04-24 08:40:23 +00:00
|
|
|
item.data.href = serviceURL + '/published/' + slug + path;
|
2019-01-29 03:03:43 +00:00
|
|
|
}
|
|
|
|
// change dir url
|
|
|
|
else if (Utils.isInternalDirLink(url, repoID)) {
|
2019-01-30 03:48:15 +00:00
|
|
|
let path = Utils.getPathFromInternalDirLink(url, repoID);
|
2019-01-29 03:03:43 +00:00
|
|
|
// replace url
|
2019-04-24 08:40:23 +00:00
|
|
|
item.data.href = serviceURL + '/published/' + slug + path;
|
|
|
|
}
|
2019-01-29 03:03:43 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return item;
|
|
|
|
}
|
|
|
|
|
|
|
|
modifyValueBeforeRender = (value) => {
|
|
|
|
let nodes = value.document.nodes;
|
|
|
|
let newNodes = Utils.changeMarkdownNodes(nodes, this.changeInlineNode);
|
|
|
|
value.document.nodes = newNodes;
|
|
|
|
return value;
|
2019-01-15 05:22:00 +00:00
|
|
|
}
|
|
|
|
|
2019-04-28 07:38:21 +00:00
|
|
|
getRootNode = () => {
|
|
|
|
let value = deserialize(this.props.indexContent);
|
|
|
|
value = value.toJSON();
|
|
|
|
value = this.modifyValueBeforeRender(value);
|
|
|
|
value = Value.fromJSON(value);
|
|
|
|
const nodes = value.document.nodes;
|
|
|
|
nodes.map((node) => {
|
|
|
|
if (node.type ==='unordered_list' || node.type === 'ordered_list') {
|
|
|
|
this.rootNode = node;
|
|
|
|
}
|
|
|
|
});
|
2019-01-28 08:48:03 +00:00
|
|
|
}
|
|
|
|
|
2019-01-15 05:22:00 +00:00
|
|
|
render() {
|
2019-04-28 07:38:21 +00:00
|
|
|
return <FolderItem rootNode={this.rootNode} bindClickEvent={this.bindClickEvent} hasChild={false}/>;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
IndexContentViewer.propTypes = viewerPropTypes;
|
|
|
|
|
|
|
|
const FolderItemPropTypes = {
|
|
|
|
rootNode: PropTypes.object.isRequired,
|
|
|
|
bindClickEvent: PropTypes.func.isRequired,
|
|
|
|
hasChild: PropTypes.bool,
|
|
|
|
};
|
|
|
|
|
|
|
|
class FolderItem extends React.Component {
|
|
|
|
|
|
|
|
constructor(props) {
|
|
|
|
super(props);
|
|
|
|
this.state = {
|
|
|
|
showChild: true
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
toggleShowChild = () => {
|
|
|
|
this.setState({showChild: !this.state.showChild}, () => {
|
|
|
|
if (this.state.showChild) {
|
|
|
|
this.props.bindClickEvent();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
componentDidMount() {
|
|
|
|
if (this.props.hasChild) {
|
|
|
|
this.setState({ showChild: false });
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
render() {
|
|
|
|
const { rootNode } = this.props;
|
2019-01-15 05:22:00 +00:00
|
|
|
return (
|
2019-04-28 07:38:21 +00:00
|
|
|
<div className="folder-item px-2">
|
|
|
|
{
|
|
|
|
rootNode.nodes.map((node) => {
|
|
|
|
if (node.type === 'paragraph') {
|
|
|
|
let href = '';
|
|
|
|
node.nodes.map((linkNode) => {
|
|
|
|
// deal with node isn't a link
|
|
|
|
href = linkNode.data ? encodeURI(linkNode.data.get('href')) : 'javascript:void(0);';
|
|
|
|
});
|
|
|
|
return (
|
|
|
|
<div key={node.key} className="px-4 wiki-nav-content">
|
|
|
|
<a href={href}>{node.text}</a>
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
const hasChild = (rootNode.type === 'unordered_list' && rootNode.nodes.size > 1);
|
|
|
|
return (
|
|
|
|
<React.Fragment key={node.key}>
|
|
|
|
{this.props.hasChild &&
|
|
|
|
<span className="switch-btn" onClick={this.toggleShowChild}>
|
|
|
|
{this.state.showChild ? <i className="fa fa-caret-down"></i> : <i className="fa fa-caret-right"></i>}
|
|
|
|
</span>
|
|
|
|
}
|
|
|
|
{this.state.showChild &&
|
|
|
|
<FolderItem
|
|
|
|
rootNode={node}
|
|
|
|
key={node.key}
|
|
|
|
hasChild={hasChild}
|
|
|
|
bindClickEvent={this.props.bindClickEvent}
|
|
|
|
/>
|
|
|
|
}
|
|
|
|
</React.Fragment>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
2019-01-15 05:22:00 +00:00
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-28 07:38:21 +00:00
|
|
|
FolderItem.propTypes = FolderItemPropTypes;
|
2019-01-15 05:22:00 +00:00
|
|
|
|
|
|
|
export default IndexContentViewer;
|