Skip to content

Commit 5100b0f

Browse files
committed
Visualize local codes
1 parent c3c6ded commit 5100b0f

File tree

10 files changed

+131
-37
lines changed

10 files changed

+131
-37
lines changed

package-lock.json

Lines changed: 25 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@
7272
"node-sass": "^4.5.3",
7373
"postcss-flexboxfixer": "0.0.5",
7474
"postcss-loader": "^2.0.6",
75+
"query-string": "^6.2.0",
7576
"raw-loader": "^0.5.1",
7677
"react": "^16.3.1",
7778
"react-ace": "^6.1.4",

src/backend/controllers/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
export { default as auth } from './auth';
22
export { default as algorithms } from './algorithms';
33
export { default as tracers } from './tracers';
4+
export { default as visualizations } from './visualizations';

src/backend/controllers/tracers.js

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ const router = express.Router();
1414
const trace = lang => (req, res, next) => {
1515
const { code } = req.body;
1616
const tempPath = path.resolve(__dirname, '..', 'public', 'codes', uuid.v4());
17-
const tracesPath = path.resolve(tempPath, 'traces.json');
1817
fs.outputFile(path.resolve(tempPath, `Main.${lang}`), code)
1918
.then(() => {
2019
const builder = builderMap[lang];
@@ -37,11 +36,13 @@ const trace = lang => (req, res, next) => {
3736
throw error;
3837
}).finally(() => clearTimeout(timer));
3938
})
40-
.then(() => fs.pathExists(tracesPath))
41-
.then(exists => {
42-
if (!exists) throw new Error('Traces Not Found');
43-
res.sendFile(tracesPath);
44-
})
39+
.then(() => new Promise((resolve, reject) => {
40+
const visualizationPath = path.resolve(tempPath, 'traces.json');
41+
res.sendFile(visualizationPath, err => {
42+
if (err) return reject(new Error('Visualization Not Found'));
43+
resolve();
44+
});
45+
}))
4546
.catch(next)
4647
.finally(() => fs.remove(tempPath));
4748
};
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import express from 'express';
2+
import path from 'path';
3+
import uuid from 'uuid';
4+
import fs from 'fs-extra';
5+
import Promise from 'bluebird';
6+
7+
const router = express.Router();
8+
9+
const uploadPath = path.resolve(__dirname, '..', 'public', 'visualizations');
10+
const getVisualizationPath = visualizationId => path.resolve(uploadPath, `${visualizationId}.json`);
11+
12+
fs.remove(uploadPath).catch(console.error);
13+
14+
const uploadVisualization = (req, res, next) => {
15+
const { content } = req.body;
16+
const visualizationId = uuid.v4();
17+
const tracesPath = getVisualizationPath(visualizationId);
18+
const url = `https://algorithm-visualizer.org/scratch-paper/new?visualizationId=${visualizationId}`;
19+
fs.outputFile(tracesPath, content)
20+
.then(() => res.send(url))
21+
.catch(next);
22+
};
23+
24+
const getVisualization = (req, res, next) => {
25+
const { visualizationId } = req.params;
26+
const visualizationPath = getVisualizationPath(visualizationId);
27+
new Promise((resolve, reject) => {
28+
res.sendFile(visualizationPath, err => {
29+
if (err) return reject(new Error('Visualization Expired'));
30+
resolve();
31+
});
32+
}).catch(next)
33+
.finally(() => fs.remove(visualizationPath));
34+
};
35+
36+
router.route('/')
37+
.post(uploadVisualization);
38+
39+
router.route('/:visualizationId')
40+
.get(getVisualization);
41+
42+
export default router;

src/frontend/apis/index.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,10 @@ const AlgorithmApi = {
5252
getAlgorithm: GET('/algorithms/:categoryKey/:algorithmKey'),
5353
};
5454

55+
const VisualizationApi = {
56+
getVisualization: GET('/visualizations/:visualizationId'),
57+
};
58+
5559
const GitHubApi = {
5660
auth: token => Promise.resolve(axios.defaults.headers.common['Authorization'] = token && `token ${token}`),
5761
getUser: GET('https://api.github.com/user'),
@@ -73,6 +77,7 @@ const TracerApi = {
7377
method: 'set',
7478
args: [code],
7579
}]),
80+
json: ({ code }) => new Promise(resolve => resolve(JSON.parse(code))),
7681
js: ({ code }, params, cancelToken) => new Promise((resolve, reject) => {
7782
const worker = new Worker('/api/tracers/js');
7883
if (cancelToken) {
@@ -97,6 +102,7 @@ const TracerApi = {
97102

98103
export {
99104
AlgorithmApi,
105+
VisualizationApi,
100106
GitHubApi,
101107
TracerApi,
102108
};

src/frontend/common/util.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,21 @@ const refineGist = gist => {
2121
return { login, gistId, title, files };
2222
};
2323

24+
const createFile = (name, content, contributors) => ({ name, content, contributors });
25+
26+
const createProjectFile = (name, content) => createFile(name, content, [{
27+
login: 'algorithm-visualizer',
28+
avatar_url: 'https://github.com/algorithm-visualizer.png',
29+
}]);
30+
31+
const createUserFile = (name, content) => createFile(name, content, undefined);
32+
2433
export {
2534
classes,
2635
distance,
2736
extension,
2837
refineGist,
38+
createFile,
39+
createProjectFile,
40+
createUserFile,
2941
};

src/frontend/components/App/index.jsx

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { connect } from 'react-redux';
44
import Promise from 'bluebird';
55
import { Helmet } from 'react-helmet';
66
import AutosizeInput from 'react-input-autosize';
7+
import queryString from 'query-string';
78
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
89
import faPlus from '@fortawesome/fontawesome-free-solid/faPlus';
910
import {
@@ -16,9 +17,9 @@ import {
1617
ToastContainer,
1718
VisualizationViewer,
1819
} from '/components';
19-
import { AlgorithmApi, GitHubApi } from '/apis';
20+
import { AlgorithmApi, GitHubApi, VisualizationApi } from '/apis';
2021
import { actions } from '/reducers';
21-
import { extension, refineGist } from '/common/util';
22+
import { createUserFile, extension, refineGist } from '/common/util';
2223
import { exts, languages } from '/common/config';
2324
import { CONTRIBUTING_MD } from '/files';
2425
import styles from './stylesheet.scss';
@@ -43,7 +44,9 @@ class App extends BaseComponent {
4344
window.signIn = this.signIn.bind(this);
4445
window.signOut = this.signOut.bind(this);
4546

46-
this.loadAlgorithm(this.props.match.params);
47+
const { params } = this.props.match;
48+
const { search } = this.props.location;
49+
this.loadAlgorithm(params, queryString.parse(search));
4750

4851
const accessToken = Cookies.get('access_token');
4952
if (accessToken) this.signIn(accessToken);
@@ -64,12 +67,13 @@ class App extends BaseComponent {
6467

6568
componentWillReceiveProps(nextProps) {
6669
const { params } = nextProps.match;
67-
if (params !== this.props.match.params) {
70+
const { search } = nextProps.location;
71+
if (params !== this.props.match.params || search !== this.props.location.search) {
6872
const { categoryKey, algorithmKey, gistId } = params;
6973
const { algorithm, scratchPaper } = nextProps.current;
7074
if (algorithm && algorithm.categoryKey === categoryKey && algorithm.algorithmKey === algorithmKey) return;
7175
if (scratchPaper && scratchPaper.gistId === gistId) return;
72-
this.loadAlgorithm(params);
76+
this.loadAlgorithm(params, queryString.parse(search));
7377
}
7478
}
7579

@@ -138,7 +142,7 @@ class App extends BaseComponent {
138142
.catch(this.handleError);
139143
}
140144

141-
loadAlgorithm({ categoryKey, algorithmKey, gistId }) {
145+
loadAlgorithm({ categoryKey, algorithmKey, gistId }, { visualizationId }) {
142146
const { ext } = this.props.env;
143147
const fetch = () => {
144148
if (window.__PRELOADED_ALGORITHM__) {
@@ -147,6 +151,16 @@ class App extends BaseComponent {
147151
} else if (categoryKey && algorithmKey) {
148152
return AlgorithmApi.getAlgorithm(categoryKey, algorithmKey)
149153
.then(({ algorithm }) => this.props.setAlgorithm(algorithm));
154+
} else if (gistId === 'new' && visualizationId) {
155+
return VisualizationApi.getVisualization(visualizationId)
156+
.then(content => {
157+
this.props.setScratchPaper({
158+
login: undefined,
159+
gistId,
160+
title: 'Untitled',
161+
files: [CONTRIBUTING_MD, createUserFile('traces.json', JSON.stringify(content))],
162+
});
163+
});
150164
} else if (gistId === 'new') {
151165
const language = languages.find(language => language.ext === ext);
152166
this.props.setScratchPaper({
@@ -175,7 +189,8 @@ class App extends BaseComponent {
175189
selectDefaultTab() {
176190
const { ext } = this.props.env;
177191
const { files } = this.props.current;
178-
let editorTabIndex = files.findIndex(file => extension(file.name) === ext);
192+
let editorTabIndex = files.findIndex(file => extension(file.name) === 'json');
193+
if (!~editorTabIndex) files.findIndex(file => extension(file.name) === ext);
179194
if (!~editorTabIndex) editorTabIndex = files.findIndex(file => exts.includes(extension(file.name)));
180195
if (!~editorTabIndex) editorTabIndex = Math.min(0, files.length - 1);
181196
this.handleChangeEditorTabIndex(editorTabIndex);

src/frontend/components/CodeEditor/index.jsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import React from 'react';
22
import AceEditor from 'react-ace';
33
import 'brace/mode/plain_text';
44
import 'brace/mode/markdown';
5+
import 'brace/mode/json';
56
import 'brace/mode/javascript';
67
import 'brace/mode/c_cpp';
78
import 'brace/mode/java';
@@ -43,7 +44,10 @@ class CodeEditor extends React.Component {
4344

4445
const fileExt = extension(file.name);
4546
const language = languages.find(language => language.ext === fileExt);
46-
const mode = language ? language.mode : fileExt === 'md' ? 'markdown' : 'plain_text';
47+
const mode = language ? language.mode :
48+
fileExt === 'md' ? 'markdown' :
49+
fileExt === 'json' ? 'json' :
50+
'plain_text';
4751

4852
return (
4953
<div className={classes(styles.code_editor, className)}>

src/frontend/files/index.js

Lines changed: 10 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,12 @@
1-
const createProjectFile = filePath => ({
2-
name: filePath.split('/').pop(),
3-
content: require('raw-loader!./' + filePath),
4-
contributors: [{
5-
login: 'algorithm-visualizer',
6-
avatar_url: 'https://github.com/algorithm-visualizer.png',
7-
}],
8-
});
1+
import { createProjectFile, createUserFile } from '/common/util';
92

10-
const createUserFile = filePath => ({
11-
name: filePath.split('/').pop(),
12-
content: require('raw-loader!./' + filePath),
13-
contributors: undefined,
14-
});
3+
const getName = filePath => filePath.split('/').pop();
4+
const getContent = filePath => require('raw-loader!./' + filePath);
5+
const readProjectFile = filePath => createProjectFile(getName(filePath), getContent(filePath));
6+
const readUserFile = filePath => createUserFile(getName(filePath), getContent(filePath));
157

16-
export const CODE_CPP = createUserFile('skeletons/code.cpp');
17-
export const CODE_JAVA = createUserFile('skeletons/code.java');
18-
export const CODE_JS = createUserFile('skeletons/code.js');
19-
export const README_MD = createProjectFile('algorithm-visualizer/README.md');
20-
export const CONTRIBUTING_MD = createProjectFile('scratch-paper/CONTRIBUTING.md');
8+
export const CODE_CPP = readUserFile('skeletons/code.cpp');
9+
export const CODE_JAVA = readUserFile('skeletons/code.java');
10+
export const CODE_JS = readUserFile('skeletons/code.js');
11+
export const README_MD = readProjectFile('algorithm-visualizer/README.md');
12+
export const CONTRIBUTING_MD = readProjectFile('scratch-paper/CONTRIBUTING.md');

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy