提交 5100b0f3 编写于 作者: J Jason Park

Visualize local codes

上级 c3c6dedb
......@@ -8534,6 +8534,18 @@
"prepend-http": "^1.0.0",
"query-string": "^4.1.0",
"sort-keys": "^1.0.0"
},
"dependencies": {
"query-string": {
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/query-string/-/query-string-4.3.4.tgz",
"integrity": "sha1-u7aTucqRXCMlFbIosaArYJBD2+s=",
"dev": true,
"requires": {
"object-assign": "^4.1.0",
"strict-uri-encode": "^1.0.0"
}
}
}
},
"npm-conf": {
......@@ -10131,13 +10143,21 @@
"integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA=="
},
"query-string": {
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/query-string/-/query-string-4.3.4.tgz",
"integrity": "sha1-u7aTucqRXCMlFbIosaArYJBD2+s=",
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/query-string/-/query-string-6.2.0.tgz",
"integrity": "sha512-5wupExkIt8RYL4h/FE+WTg3JHk62e6fFPWtAZA9J5IWK1PfTfKkMS93HBUHcFpeYi9KsY5pFbh+ldvEyaz5MyA==",
"dev": true,
"requires": {
"object-assign": "^4.1.0",
"strict-uri-encode": "^1.0.0"
"decode-uri-component": "^0.2.0",
"strict-uri-encode": "^2.0.0"
},
"dependencies": {
"strict-uri-encode": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz",
"integrity": "sha1-ucczDHBChi9rFC3CdLvMWGbONUY=",
"dev": true
}
}
},
"querystring": {
......
export { default as auth } from './auth';
export { default as algorithms } from './algorithms';
export { default as tracers } from './tracers';
export { default as visualizations } from './visualizations';
......@@ -14,7 +14,6 @@ const router = express.Router();
const trace = lang => (req, res, next) => {
const { code } = req.body;
const tempPath = path.resolve(__dirname, '..', 'public', 'codes', uuid.v4());
const tracesPath = path.resolve(tempPath, 'traces.json');
fs.outputFile(path.resolve(tempPath, `Main.${lang}`), code)
.then(() => {
const builder = builderMap[lang];
......@@ -37,11 +36,13 @@ const trace = lang => (req, res, next) => {
throw error;
}).finally(() => clearTimeout(timer));
})
.then(() => fs.pathExists(tracesPath))
.then(exists => {
if (!exists) throw new Error('Traces Not Found');
res.sendFile(tracesPath);
})
.then(() => new Promise((resolve, reject) => {
const visualizationPath = path.resolve(tempPath, 'traces.json');
res.sendFile(visualizationPath, err => {
if (err) return reject(new Error('Visualization Not Found'));
resolve();
});
}))
.catch(next)
.finally(() => fs.remove(tempPath));
};
......
import express from 'express';
import path from 'path';
import uuid from 'uuid';
import fs from 'fs-extra';
import Promise from 'bluebird';
const router = express.Router();
const uploadPath = path.resolve(__dirname, '..', 'public', 'visualizations');
const getVisualizationPath = visualizationId => path.resolve(uploadPath, `${visualizationId}.json`);
fs.remove(uploadPath).catch(console.error);
const uploadVisualization = (req, res, next) => {
const { content } = req.body;
const visualizationId = uuid.v4();
const tracesPath = getVisualizationPath(visualizationId);
const url = `https://algorithm-visualizer.org/scratch-paper/new?visualizationId=${visualizationId}`;
fs.outputFile(tracesPath, content)
.then(() => res.send(url))
.catch(next);
};
const getVisualization = (req, res, next) => {
const { visualizationId } = req.params;
const visualizationPath = getVisualizationPath(visualizationId);
new Promise((resolve, reject) => {
res.sendFile(visualizationPath, err => {
if (err) return reject(new Error('Visualization Expired'));
resolve();
});
}).catch(next)
.finally(() => fs.remove(visualizationPath));
};
router.route('/')
.post(uploadVisualization);
router.route('/:visualizationId')
.get(getVisualization);
export default router;
......@@ -52,6 +52,10 @@ const AlgorithmApi = {
getAlgorithm: GET('/algorithms/:categoryKey/:algorithmKey'),
};
const VisualizationApi = {
getVisualization: GET('/visualizations/:visualizationId'),
};
const GitHubApi = {
auth: token => Promise.resolve(axios.defaults.headers.common['Authorization'] = token && `token ${token}`),
getUser: GET('https://api.github.com/user'),
......@@ -73,6 +77,7 @@ const TracerApi = {
method: 'set',
args: [code],
}]),
json: ({ code }) => new Promise(resolve => resolve(JSON.parse(code))),
js: ({ code }, params, cancelToken) => new Promise((resolve, reject) => {
const worker = new Worker('/api/tracers/js');
if (cancelToken) {
......@@ -97,6 +102,7 @@ const TracerApi = {
export {
AlgorithmApi,
VisualizationApi,
GitHubApi,
TracerApi,
};
......@@ -21,9 +21,21 @@ const refineGist = gist => {
return { login, gistId, title, files };
};
const createFile = (name, content, contributors) => ({ name, content, contributors });
const createProjectFile = (name, content) => createFile(name, content, [{
login: 'algorithm-visualizer',
avatar_url: 'https://github.com/algorithm-visualizer.png',
}]);
const createUserFile = (name, content) => createFile(name, content, undefined);
export {
classes,
distance,
extension,
refineGist,
createFile,
createProjectFile,
createUserFile,
};
......@@ -4,6 +4,7 @@ import { connect } from 'react-redux';
import Promise from 'bluebird';
import { Helmet } from 'react-helmet';
import AutosizeInput from 'react-input-autosize';
import queryString from 'query-string';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import faPlus from '@fortawesome/fontawesome-free-solid/faPlus';
import {
......@@ -16,9 +17,9 @@ import {
ToastContainer,
VisualizationViewer,
} from '/components';
import { AlgorithmApi, GitHubApi } from '/apis';
import { AlgorithmApi, GitHubApi, VisualizationApi } from '/apis';
import { actions } from '/reducers';
import { extension, refineGist } from '/common/util';
import { createUserFile, extension, refineGist } from '/common/util';
import { exts, languages } from '/common/config';
import { CONTRIBUTING_MD } from '/files';
import styles from './stylesheet.scss';
......@@ -43,7 +44,9 @@ class App extends BaseComponent {
window.signIn = this.signIn.bind(this);
window.signOut = this.signOut.bind(this);
this.loadAlgorithm(this.props.match.params);
const { params } = this.props.match;
const { search } = this.props.location;
this.loadAlgorithm(params, queryString.parse(search));
const accessToken = Cookies.get('access_token');
if (accessToken) this.signIn(accessToken);
......@@ -64,12 +67,13 @@ class App extends BaseComponent {
componentWillReceiveProps(nextProps) {
const { params } = nextProps.match;
if (params !== this.props.match.params) {
const { search } = nextProps.location;
if (params !== this.props.match.params || search !== this.props.location.search) {
const { categoryKey, algorithmKey, gistId } = params;
const { algorithm, scratchPaper } = nextProps.current;
if (algorithm && algorithm.categoryKey === categoryKey && algorithm.algorithmKey === algorithmKey) return;
if (scratchPaper && scratchPaper.gistId === gistId) return;
this.loadAlgorithm(params);
this.loadAlgorithm(params, queryString.parse(search));
}
}
......@@ -138,7 +142,7 @@ class App extends BaseComponent {
.catch(this.handleError);
}
loadAlgorithm({ categoryKey, algorithmKey, gistId }) {
loadAlgorithm({ categoryKey, algorithmKey, gistId }, { visualizationId }) {
const { ext } = this.props.env;
const fetch = () => {
if (window.__PRELOADED_ALGORITHM__) {
......@@ -147,6 +151,16 @@ class App extends BaseComponent {
} else if (categoryKey && algorithmKey) {
return AlgorithmApi.getAlgorithm(categoryKey, algorithmKey)
.then(({ algorithm }) => this.props.setAlgorithm(algorithm));
} else if (gistId === 'new' && visualizationId) {
return VisualizationApi.getVisualization(visualizationId)
.then(content => {
this.props.setScratchPaper({
login: undefined,
gistId,
title: 'Untitled',
files: [CONTRIBUTING_MD, createUserFile('traces.json', JSON.stringify(content))],
});
});
} else if (gistId === 'new') {
const language = languages.find(language => language.ext === ext);
this.props.setScratchPaper({
......@@ -175,7 +189,8 @@ class App extends BaseComponent {
selectDefaultTab() {
const { ext } = this.props.env;
const { files } = this.props.current;
let editorTabIndex = files.findIndex(file => extension(file.name) === ext);
let editorTabIndex = files.findIndex(file => extension(file.name) === 'json');
if (!~editorTabIndex) files.findIndex(file => extension(file.name) === ext);
if (!~editorTabIndex) editorTabIndex = files.findIndex(file => exts.includes(extension(file.name)));
if (!~editorTabIndex) editorTabIndex = Math.min(0, files.length - 1);
this.handleChangeEditorTabIndex(editorTabIndex);
......
......@@ -2,6 +2,7 @@ import React from 'react';
import AceEditor from 'react-ace';
import 'brace/mode/plain_text';
import 'brace/mode/markdown';
import 'brace/mode/json';
import 'brace/mode/javascript';
import 'brace/mode/c_cpp';
import 'brace/mode/java';
......@@ -43,7 +44,10 @@ class CodeEditor extends React.Component {
const fileExt = extension(file.name);
const language = languages.find(language => language.ext === fileExt);
const mode = language ? language.mode : fileExt === 'md' ? 'markdown' : 'plain_text';
const mode = language ? language.mode :
fileExt === 'md' ? 'markdown' :
fileExt === 'json' ? 'json' :
'plain_text';
return (
<div className={classes(styles.code_editor, className)}>
......
const createProjectFile = filePath => ({
name: filePath.split('/').pop(),
content: require('raw-loader!./' + filePath),
contributors: [{
login: 'algorithm-visualizer',
avatar_url: 'https://github.com/algorithm-visualizer.png',
}],
});
import { createProjectFile, createUserFile } from '/common/util';
const createUserFile = filePath => ({
name: filePath.split('/').pop(),
content: require('raw-loader!./' + filePath),
contributors: undefined,
});
const getName = filePath => filePath.split('/').pop();
const getContent = filePath => require('raw-loader!./' + filePath);
const readProjectFile = filePath => createProjectFile(getName(filePath), getContent(filePath));
const readUserFile = filePath => createUserFile(getName(filePath), getContent(filePath));
export const CODE_CPP = createUserFile('skeletons/code.cpp');
export const CODE_JAVA = createUserFile('skeletons/code.java');
export const CODE_JS = createUserFile('skeletons/code.js');
export const README_MD = createProjectFile('algorithm-visualizer/README.md');
export const CONTRIBUTING_MD = createProjectFile('scratch-paper/CONTRIBUTING.md');
export const CODE_CPP = readUserFile('skeletons/code.cpp');
export const CODE_JAVA = readUserFile('skeletons/code.java');
export const CODE_JS = readUserFile('skeletons/code.js');
export const README_MD = readProjectFile('algorithm-visualizer/README.md');
export const CONTRIBUTING_MD = readProjectFile('scratch-paper/CONTRIBUTING.md');
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册