diff --git a/client/app/components/Pages/CodeEditor.js b/client/app/components/Pages/CodeEditor.js
new file mode 100644
index 0000000..bad7004
--- /dev/null
+++ b/client/app/components/Pages/CodeEditor.js
@@ -0,0 +1,52 @@
+import React, { Component } from 'react'
+import TextIDE from "../common/TextIDE";
+import sampleFunction from '../common/TextIDE/sampleFunction';
+
+export default class CodeEditor extends Component {
+ render() {
+ return (
+
+
+
+
+ )
+ }
+}
+
+/*
+DEFAULT VALUES FOR
+ width= "700px"
+ height= "600px"
+ languages = ['C','C++','Python 2','Python 3','Java']
+ defaultCode = '' for all languages defined above
+ fontSize = '12pt'
+ fontSizes = {['12pt','14pt','16pt']}
+ theme = 'vs'
+ readOnly = {false}
+ showToolbar= {true}
+ showCustomInput = {true}
+ showRunButton = {true}
+ showSubmitButton = {true}
+ runFunction = NO DEFAULTS
+ submitFunction = NO DEFAULTS
+*/
\ No newline at end of file
diff --git a/client/app/components/common/TextIDE/RunButton.js b/client/app/components/common/TextIDE/RunButton.js
new file mode 100644
index 0000000..54d5df0
--- /dev/null
+++ b/client/app/components/common/TextIDE/RunButton.js
@@ -0,0 +1,52 @@
+import React,{Component} from 'react';
+import {Button} from 'reactstrap';
+
+
+class RunCodeButton extends Component {
+
+ constructor(props){
+ super(props);
+
+ this.submitCode = this.submitCode.bind(this);
+ }
+
+ submitCode(event) {
+ event.preventDefault()
+
+ const codeText = this.props.editor.getValue()
+ const codeLanguage = this.props.language.toLowerCase()
+ const isCustomInput = this.props.isCustomInput
+ var stdin = ''
+
+ if(isCustomInput){
+ stdin = document.getElementById('customInput').value
+ }
+
+ var data = {
+ content: codeText,
+ language: codeLanguage,
+ customInput: isCustomInput,
+ stdin: stdin,
+ submit: false
+ }
+ if(this.props.customFunction === undefined){
+ console.error('No Run Function is defined for the TextIDE Component')
+ }
+ else {
+ this.props.customFunction(data)
+ }
+ }
+
+ render(){
+ return (
+
+
+
+ )
+ }
+
+}
+
+export default RunCodeButton;
\ No newline at end of file
diff --git a/client/app/components/common/TextIDE/SubmitButton.js b/client/app/components/common/TextIDE/SubmitButton.js
new file mode 100644
index 0000000..55f5387
--- /dev/null
+++ b/client/app/components/common/TextIDE/SubmitButton.js
@@ -0,0 +1,68 @@
+import React,{Component} from 'react'
+import { Button, Modal, ModalHeader, ModalBody, ModalFooter } from 'reactstrap'
+
+class SubmitCodeButton extends Component {
+
+ constructor(props){
+ super(props);
+ this.state = {
+ showConfirmation: false,
+ }
+ this.submitCode = this.submitCode.bind(this);
+ this.toggleConfirmation = this.toggleConfirmation.bind(this);
+ }
+
+ toggleConfirmation(){
+ this.setState(prevState => ({
+ showConfirmation: !prevState.showConfirmation
+ }))
+ }
+
+ submitCode(event) {
+ event.preventDefault()
+ this.toggleConfirmation()
+
+ const codeText = this.props.editor.getValue()
+ const codeLanguage = this.props.language.toLowerCase()
+
+ var data = {
+ content: codeText,
+ language: codeLanguage,
+ customInput: false,
+ stdin: '',
+ submit: true
+ }
+
+ if(this.props.customFunction === undefined){
+ console.error('No Submit Function is defined for the TextIDE Component')
+ }
+ else {
+ this.props.customFunction(data)
+ }
+ }
+
+ render(){
+ return (
+
+
+
+
+
+ Confirm to Submit
+
+ Are you sure you want to submit?
+
+
+
+
+
+
+
+ )
+ }
+
+}
+
+export default SubmitCodeButton;
\ No newline at end of file
diff --git a/client/app/components/common/TextIDE/TextIDE.js b/client/app/components/common/TextIDE/TextIDE.js
new file mode 100644
index 0000000..f4127bd
--- /dev/null
+++ b/client/app/components/common/TextIDE/TextIDE.js
@@ -0,0 +1,294 @@
+import React from 'react';
+import Toolbar from './Toolbar';
+import RunCodeButton from './RunButton';
+import SubmitCodeButton from './SubmitButton';
+import MonacoEditor from 'react-monaco-editor';
+import { InputGroup, InputGroupText, Input } from 'reactstrap';
+
+
+
+const buttonRowStyle ={
+ marginBottom: '5px',
+ marginTop :'5px'
+
+}
+
+const bottomRowStyle ={
+ overflow: 'auto',
+ marginTop: '15px',
+ marginBottom: '5px',
+ padding: '0px',
+}
+
+const editorStyle ={
+ padding:'0px',
+ boxShadow: '0px 3px 5px black'
+}
+
+// Add(in the same format) or remove languages
+
+const languages = {
+ 'C' : 'c',
+ 'C++' : 'cpp',
+ 'Python 2' : 'python',
+ 'Python 3' : 'python',
+ 'Java' : 'java'
+}
+
+//No other themes available
+const themes = {
+ 'Light' : 'vs',
+ 'Dark' : 'vs-dark',
+ 'High Contrast' : 'hc-black'
+}
+
+// Add(in the same format) or remove fontSizes
+// First fontSize is the default fontSize
+const fontSizes = ['12pt','14pt','16pt']
+
+class TextIDE extends React.Component{
+
+ constructor(props){
+ super(props);
+ this.customLanguages = this.props.languages===undefined? Object.keys(languages):this.props.languages
+ this.languages = Object()
+
+ for (const i in this.customLanguages) {
+ if (this.customLanguages[i] in languages){
+ this.languages[this.customLanguages[i]] = languages[this.customLanguages[i]]
+ }
+ else{
+ var lang_name = this.customLanguages[i].charAt(0).toUpperCase() + this.customLanguages[i].toLowerCase().slice(1)
+ var lang_name_lowercase = this.customLanguages[i].toLowerCase()
+ this.languages[lang_name] = lang_name_lowercase
+ }
+ }
+
+ this.defaultWidth = '700px'
+ this.defaultHeight = '600px'
+ if(this.props.width===undefined){
+ this.width = this.defaultWidth
+ }else{
+ if(this.props.width.indexOf('px')>-1)
+ this.width = this.props.width
+ else
+ this.width = parseInt(this.props.width)+'px'
+ }
+ if(this.props.height===undefined){
+ this.height = this.defaultHeight
+ }else{
+ if(this.props.height.indexOf('px')>-1)
+ this.height = this.props.height
+ else
+ this.height = parseInt(this.props.height)+'px'
+ }
+
+ this.defaultCode = Object()
+ for (const lang in this.props.defaultCode){
+ var lang_name = lang.charAt(0).toUpperCase() + lang.toLowerCase().slice(1)
+ this.defaultCode[lang_name] = this.props.defaultCode[lang]
+ }
+
+ for(const lang in this.languages){
+ if (!(lang in this.defaultCode)){
+ this.defaultCode[lang] = ''
+ }
+ }
+
+ this.fontSize = this.props.fontSize===undefined? fontSizes[0]:this.props.fontSize
+ this.fontSizes = this.props.fontSizes===undefined? fontSizes:this.props.fontSizes
+ this.theme = this.props.theme===undefined? Object.values(themes)[0]:this.props.theme
+
+ this.readOnly = this.props.readOnly===undefined? false:this.props.readOnly
+ //this.showButtons = this.props.showButtons===undefined ? (!this.readOnly):(this.props.showButtons && !this.readOnly)
+ this.showRunButton = this.props.showRunButton===undefined ? (!this.readOnly): (this.props.showRunButton && !this.readOnly)
+ this.showSubmitButton = this.props.showSubmitButton===undefined ? (!this.readOnly): (this.props.showSubmitButton && !this.readOnly)
+
+ this.state = {
+ width: this.width,
+ height: this.height,
+ editorTheme: this.theme,
+ editorLanguage: Object.values(this.languages)[0],
+ editorLanguageKey: Object.keys(this.languages)[0],
+ editorFontSize: this.fontSize,
+ editorText: this.defaultCode[Object.keys(this.languages)[0]],
+ languages: this.languages,
+ themes: themes,
+ fontSizes: this.fontSizes,
+ customInput: false,
+ readOnly: this.readOnly,
+ showToolbar: this.props.showToolbar===undefined ? (!this.readOnly):(this.props.showToolbar && !this.readOnly),
+ //showButtons: this.showButtons,
+ showRunButton: this.showRunButton,
+ showSubmitButton: this.showSubmitButton,
+ showCustomInputOption: this.props.showCustomInput===undefined ? (!this.readOnly && (this.showRunButton || this.showSubmitButton)):
+ (this.props.showCustomInput && !this.readOnly && (this.showRunButton || this.showSubmitButton)),
+
+ }
+
+ this.changeEditorBackground = this.changeEditorBackground.bind(this)
+ this.changeEditorLanguage = this.changeEditorLanguage.bind(this)
+ this.changeEditorFontSize = this.changeEditorFontSize.bind(this)
+ this.editorDidMount = this.editorDidMount.bind(this)
+ this.showCustomInput = this.showCustomInput.bind(this)
+ this.resetCode = this.resetCode.bind(this)
+
+ }
+
+ changeEditorBackground(event,theme){
+ this.setState({
+ editorTheme: theme
+ })
+ }
+
+ changeEditorLanguage(event,language,languageKey){
+ this.setState({
+ editorLanguage: language,
+ editorLanguageKey: languageKey,
+ editorText: this.defaultCode[languageKey]
+ })
+ this.state.editor.getModel().setValue(this.defaultCode[languageKey])
+ }
+
+ changeEditorFontSize(event,fontsize){
+ this.setState({
+ editorFontSize: fontsize
+ })
+ }
+
+ showCustomInput(event){
+ this.setState( prevState => ({
+ customInput: !prevState.customInput
+ }))
+ }
+
+ editorDidMount(editor) {
+ this.setState({
+ editor: editor
+ });
+ editor.focus()
+ }
+
+ //Reset Code to defualt value
+ resetCode() {
+ this.state.editor.getModel().setValue(this.defaultCode[this.state.editorLanguageKey])
+ this.state.editor.focus()
+ }
+
+ componentDidMount() {
+ //Check if runFunction and submitFunction have been defined as the props..
+ if(!this.readOnly){
+ if(this.showRunButton && this.props.runFunction === undefined){
+ console.warn('Run Function is not defined for the TextIDE\nPass a function as a prop(runFunction) to the TextIDE component')
+ }
+ if(this.showSubmitButton && this.props.submitFunction === undefined){
+ console.warn('Submit Function is not defined for the TextIDE\nPass a function as a prop(submitFunction) to the TextIDE component')
+ }
+ }
+ window.addEventListener("resize", this.updateDimensions.bind(this));
+ }
+
+ //Auto-resizing of the editor
+ updateDimensions() {
+ this.state.editor.layout();
+ }
+
+ componentWillUnmount() {
+ window.removeEventListener("resize", this.updateDimensions.bind(this));
+ }
+
+ render() {
+
+ const containerStyle = {
+ padding: '15px 15px 2px 15px',
+ maxWidth: this.state.width
+ }
+
+ const editorOptions= {
+ fontSize: this.state.editorFontSize,
+ value: this.state.editorText,
+ readOnly: this.state.readOnly,
+ snippetSuggestions: true,
+ autoClosingBrackets: true,
+ autoClosingQuotes: true,
+ quickSuggestions: true,
+ copyWithSyntaxHighlighting: true,
+ formatOnPaste: true,
+ autoIndent: true,
+ }
+
+
+ return(
+
+
+ {(this.state.showToolbar) && (
+
+
+
+ )}
+
+
+
+
+
+ {(
+
+ {(this.state.showCustomInputOption) && (
+
+ )}
+
+ {(this.state.showRunButton) && (
+
+
+
+ )}
+ {(this.state.showSubmitButton) && (
+
+
+
+ )}
+
+
+ )}
+
+
+ );
+ }
+}
+
+export default TextIDE;
\ No newline at end of file
diff --git a/client/app/components/common/TextIDE/Toolbar.js b/client/app/components/common/TextIDE/Toolbar.js
new file mode 100644
index 0000000..9dbd7d3
--- /dev/null
+++ b/client/app/components/common/TextIDE/Toolbar.js
@@ -0,0 +1,219 @@
+import React,{Component} from "react";
+import { FaCaretDown, FaCog, FaUndoAlt } from "react-icons/fa";
+import { Button, ButtonGroup, UncontrolledDropdown, DropdownToggle, DropdownMenu, DropdownItem } from 'reactstrap';
+
+
+
+const toolBarStyle = {
+ height: '40px',
+ background:'#DCDCDC',
+ marginBottom: '0px',
+ boxShadow: '0px 3px 5px black'
+}
+
+const dropdownStyle = {
+ width: '120px',
+ height:'30px',
+ background: '#DCDCDC',
+ color: 'black',
+ margin: '5px',
+ boxShadow: 'none'
+}
+
+const dropdownButtonStyle = {
+ border: '1px black solid',
+ height:'28px',
+ width: '120px',
+ padding: '0px'
+}
+
+const editorSettingsStyle = {
+ maxWidth: '500px',
+ background: '#F5F5F5',
+ borderRadius: '5px',
+ paddingLeft: '2px',
+ paddingRight: '2px',
+ marginTop: '10px'
+}
+
+const editorSettingsRowStyle = {
+ padding: '10px 0 10px 0',
+}
+
+const settingButton = {
+ margin: '2px',
+ borderRadius: '2px',
+ width:'100px',
+ fontSize:'10pt',
+}
+
+const labelStyle = {
+ margin:'auto 10px auto 10px',
+ width:'100px',
+ fontSize: '12pt',
+ fontFamily: 'Monospace, Courier New'
+}
+
+const dropdownBoxStyle = {
+ minWidth:'120px',
+ maxWidth: '140px',
+ maxHeight:'150px',
+ overflowY: 'auto',
+}
+
+class Toolbar extends Component{
+
+ constructor(props){
+ super(props);
+ this.state = {
+ chosenLanguage: this.props.editorLanguage,
+ chosenLanguageShow: this.props.editorLanguageKey,
+ languages: this.props.languages,
+ themes: this.props.themes,
+ fontSizes: this.props.fontSizes
+ }
+ this.changeEditorLanguage = this.changeEditorLanguage.bind(this)
+ this.changeEditorBackground = this.changeEditorBackground.bind(this)
+ this.changeEditorFontSize = this.changeEditorFontSize.bind(this)
+ this.resetCode = this.resetCode.bind(this)
+
+ }
+
+ changeEditorBackground(event) {
+ event.preventDefault();
+ this.props.changeEditorBackground(event,event.target.id);
+ }
+
+ changeEditorLanguage(event) {
+ event.preventDefault();
+ this.props.changeEditorLanguage(event,event.target.id,event.target.value);
+ this.setState({
+ chosenLanguage: event.target.id,
+ chosenLanguageShow: event.target.value
+ });
+ }
+
+ changeEditorFontSize(event) {
+ event.preventDefault();
+ this.props.changeEditorFontSize(event,event.target.id);
+ }
+
+ //Reset Code to defualt value
+ resetCode() {
+ this.props.resetCode()
+ }
+
+
+ render(){
+ const lang_items = []
+ for(const lang in this.state.languages){
+ var lang_button =
+ {lang}
+
+ lang_items.push(lang_button);
+ }
+
+ const theme_items = []
+ for (const theme_name in this.state.themes) {
+ const btnclass = this.props.editorTheme === this.state.themes[theme_name]?'btn btn-sm btn-success':'btn btn-sm'
+ var theme_button =
+
+
+ theme_items.push(theme_button)
+ }
+
+ const font_items = []
+ for (const font_size in this.state.fontSizes) {
+ const btnclass = this.props.editorFontSize === this.state.fontSizes[font_size]?'btn btn-sm btn-success':'btn btn-sm'
+ var font_button =
+
+
+ font_items.push(font_button)
+ }
+
+ return(
+
+
+
+
+
+
+
+
+
+ {this.state.chosenLanguageShow}
+
+
+ {lang_items}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {theme_items}
+
+
+
+
+
+
+
+ {font_items}
+
+
+
+
+
+
+
+
+
+ {theme_items}
+
+
+
+
+
+
+
+ {font_items}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+ }
+}
+
+export default Toolbar
\ No newline at end of file
diff --git a/client/app/components/common/TextIDE/index.js b/client/app/components/common/TextIDE/index.js
new file mode 100644
index 0000000..ed115c6
--- /dev/null
+++ b/client/app/components/common/TextIDE/index.js
@@ -0,0 +1 @@
+export { default } from "./TextIDE";
\ No newline at end of file
diff --git a/client/app/components/common/TextIDE/sampleFunction.js b/client/app/components/common/TextIDE/sampleFunction.js
new file mode 100644
index 0000000..9c8a041
--- /dev/null
+++ b/client/app/components/common/TextIDE/sampleFunction.js
@@ -0,0 +1,23 @@
+// This is a sample function to send as prop to the runFunction or submitFunction
+
+/**
+ *
+ * @param {Object} data Object that will be passed to your function (runFunction or submitFunction)
+ * @param {string} data.content Code from the editor
+ * @param {string} data.language Language used
+ * @param {boolean} data.customInput TRUE if the user selected custom input and FALSE otherwise
+ * @param {string} data.stdin stdin for the submission if customInput is TRUE and '' if customInput is FALSE
+ * @param {boolean} data.submit TRUE if user wants to submit code and FALSE if user wants to run code
+ */
+
+export default function sampleFunction(data) {
+
+ console.log(
+ 'Content: '+ data['content'],
+ '\nLanguage: '+ data['language'],
+ '\nCustomInput: '+ data['customInput'],
+ '\nstdin: ' + data['stdin'],
+ '\nSubmit Code: ' + data['submit']
+ )
+
+}
\ No newline at end of file
diff --git a/client/app/index.js b/client/app/index.js
index e7e1fb5..04daf82 100644
--- a/client/app/index.js
+++ b/client/app/index.js
@@ -31,6 +31,7 @@ import zipFiles from './components/Pages/Assignments/zipFiles';
import updateHandle from './components/Pages/Profile/UpdateHandle';
import contribute from './components/Pages/Contribute';
import ReactLoading from './components/common/Loading'
+import CodeEditor from './components/Pages/CodeEditor'
// import 'bootstrap/dist/css/bootstrap.min.css';
@@ -79,6 +80,8 @@ render((
+
+
diff --git a/config/webpack.common.js b/config/webpack.common.js
index bbd7935..a33d2f8 100644
--- a/config/webpack.common.js
+++ b/config/webpack.common.js
@@ -3,6 +3,7 @@ const autoprefixer = require('autoprefixer');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
+const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin');
const helpers = require('./helpers');
@@ -73,6 +74,10 @@ module.exports = {
new CopyWebpackPlugin([{
from: helpers.root('client/public')
- }])
+ }]),
+
+ new MonacoWebpackPlugin({
+ languages: ['cpp','java','python']
+ })
]
};
diff --git a/package.json b/package.json
index fbf0101..81e305e 100644
--- a/package.json
+++ b/package.json
@@ -36,6 +36,7 @@
"extract-text-webpack-plugin": "^4.0.0-beta.0",
"html-webpack-plugin": "^3.1.0",
"jsonwebtoken": "^8.3.0",
+ "monaco-editor-webpack-plugin": "^1.7.0",
"mongoose": "^5.1.7",
"multer": "^1.3.1",
"node-sass": "^4.9.0",
@@ -49,8 +50,10 @@
"react-avatar": "^3.4.6",
"react-dom": "^16.2.0",
"react-hot-loader": "^4.0.0",
+ "react-icons": "^3.7.0",
"react-iframe": "^1.3.0",
"react-loading": "^2.0.3",
+ "react-monaco-editor": "~0.26.2",
"react-redux": "^5.0.7",
"react-router": "^4.2.0",
"react-router-dom": "^4.2.2",