diff --git a/README.md b/README.md
index 4862f5df..e56ce924 100644
--- a/README.md
+++ b/README.md
@@ -1,12 +1,12 @@
-[](https://heroku.com/deploy)
-# React Tutorial
-This is the React comment box example from [the React tutorial](http://facebook.github.io/react/docs/tutorial.html).
+# React Question
+
+This is questions/form builder example based on [the React tutorial](http://facebook.github.io/react/docs/tutorial.html).
## To use
-There are several simple server implementations included. They all serve static files from `public/` and handle requests to `/api/comments` to fetch or add data. Start a server with one of the following:
+There are several simple server implementations included. They all serve static files from `public/` and handle requests to `/api/questions` to fetch or add data. Start a server with Node:
### Node
@@ -15,36 +15,21 @@ npm install
node server.js
```
-### Python
+And visit .
-```sh
-pip install -r requirements.txt
-python server.py
-```
+### Webpack
-### Ruby
-```sh
-ruby server.rb
-```
+This project is using webpack to bundle javascript files.
-### PHP
```sh
-php server.php
+webpack
```
-
-### Go
-```sh
-go run server.go
-```
-
-### Perl
+or
```sh
-cpan Mojolicious
-perl server.pl
+npm build
```
-And visit . Try opening multiple tabs!
## Changing the port
diff --git a/comments.json b/comments.json
deleted file mode 100644
index 7bef77ad..00000000
--- a/comments.json
+++ /dev/null
@@ -1,12 +0,0 @@
-[
- {
- "id": 1388534400000,
- "author": "Pete Hunt",
- "text": "Hey there!"
- },
- {
- "id": 1420070400000,
- "author": "Paul O’Shannessy",
- "text": "React is *great*!"
- }
-]
diff --git a/package.json b/package.json
index e7491981..1f4bf187 100644
--- a/package.json
+++ b/package.json
@@ -1,16 +1,26 @@
{
- "name": "react-tutorial",
+ "name": "react-questions",
"version": "0.0.0",
- "description": "Code from the React tutorial.",
+ "description": "Example code questions app.",
"main": "server.js",
"dependencies": {
"body-parser": "^1.4.3",
"express": "^4.4.5"
},
- "devDependencies": {},
+ "devDependencies": {
+ "babel-core": "^6.5.2",
+ "babel-loader": "^6.2.3",
+ "babel-preset-es2015": "^6.5.0",
+ "babel-preset-react": "^6.5.0",
+ "jquery": "^2.2.0",
+ "react": "^0.14.7",
+ "react-dom": "^0.14.7",
+ "webpack": "^1.12.13"
+ },
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
- "start": "node server.js"
+ "build": "webpack --watch --colors --progress",
+ "start": "node server.js"
},
"repository": {
"type": "git",
@@ -22,12 +32,9 @@
"comment",
"example"
],
- "author": "petehunt",
+ "author": "nicrocs",
"bugs": {
- "url": "https://github.com/reactjs/react-tutorial/issues"
+ "url": "https://github.com/nicrocs"
},
- "homepage": "https://github.com/reactjs/react-tutorial",
- "engines" : {
- "node" : "0.12.x"
- }
+ "homepage": "https://github.com/nicrocs/react-tutorial",
}
diff --git a/public/css/base.css b/public/css/base.css
index bf382be3..60942bd2 100644
--- a/public/css/base.css
+++ b/public/css/base.css
@@ -4,7 +4,7 @@ body {
font-size: 15px;
line-height: 1.7;
margin: 0;
- padding: 30px;
+ padding:30px;
}
a {
@@ -60,3 +60,40 @@ p, ul {
ul {
padding-left: 30px;
}
+.questionFormColumn {
+ width:25%;
+ float:left;
+}
+.questionListColumn {
+ width:75%;
+ float:right;
+}
+.questionLabel {
+ display:block;
+}
+.choice label {
+ display:inline-block;
+}
+label + input[type="radio"], label + input[type="checkbox"] {
+ display:inline-block;
+ vertical-align:middle;
+}
+.questionForm {
+ width: 300px;
+}
+.questionForm select, .questionForm input[type="text"] {
+ width:100%;
+ display:block;
+ margin-bottom:10px;
+ padding:5px;
+ box-sizing:border-box;
+}
+.question {
+ position:relative;
+ margin-bottom:20px;
+}
+.remove {
+ position:absolute;
+ right:0;
+ top:-5px;
+}
diff --git a/public/index.html b/public/index.html
index c6494446..9bf89507 100644
--- a/public/index.html
+++ b/public/index.html
@@ -2,21 +2,12 @@
- React Tutorial
-
+ React Questions
-
-
-
-
-
+
-
-
+
diff --git a/public/scripts/components/Choice.js b/public/scripts/components/Choice.js
new file mode 100644
index 00000000..07942ef3
--- /dev/null
+++ b/public/scripts/components/Choice.js
@@ -0,0 +1,11 @@
+import React from 'react';
+var Choice = function(props) {
+ return (
+
+
+ {props.text}
+
+ );
+};
+
+export default Choice;
diff --git a/public/scripts/components/ChoiceInput.js b/public/scripts/components/ChoiceInput.js
new file mode 100644
index 00000000..5f3657ca
--- /dev/null
+++ b/public/scripts/components/ChoiceInput.js
@@ -0,0 +1,34 @@
+import React from 'react';
+var ChoiceInput = React.createClass({
+ addChoiceInput: function() {
+ this.props.addChoice();
+ },
+ handleTextChange: function(e, key) {
+ this.props.choiceTextChange(e.target.value, key);
+ },
+ removeChoice: function(e, key) {
+ this.props.removeChoice(key);
+ },
+ render:function() {
+ var choices = this.props.choices.map(function(choice) {
+ return (
+
+ this.handleTextChange(e, choice.id)}
+ />
+ this.removeChoice(e, choice.id)} className="remove">x
+
)
+ }, this);
+ return (
+
+ {choices}
+ Add Choice
+
+ );
+ }
+});
+
+export default ChoiceInput;
diff --git a/public/scripts/components/LongAnswer.js b/public/scripts/components/LongAnswer.js
new file mode 100644
index 00000000..2eabc569
--- /dev/null
+++ b/public/scripts/components/LongAnswer.js
@@ -0,0 +1,17 @@
+import React from 'react';
+var LongAnswer = function(props) {
+ return(
+
+
+ {props.label}
+
+
+ { props.id &&
+ x
+ }
+
+ );
+
+};
+
+export default LongAnswer;
diff --git a/public/scripts/components/MultipleChoice.js b/public/scripts/components/MultipleChoice.js
new file mode 100644
index 00000000..2b4e55e8
--- /dev/null
+++ b/public/scripts/components/MultipleChoice.js
@@ -0,0 +1,25 @@
+import React from 'react';
+import Choice from './Choice.js';
+var MultipleChoice = function(props) {
+ var choiceNodes = props.choices.map(function(choice) {
+ return (
+
+
+ );
+ });
+ return (
+
+
+
+ {props.label}
+
+ {choiceNodes}
+
+ { props.id &&
+ x
+ }
+
+ );
+};
+
+export default MultipleChoice;
diff --git a/public/scripts/components/Question.js b/public/scripts/components/Question.js
new file mode 100644
index 00000000..4bc03ea8
--- /dev/null
+++ b/public/scripts/components/Question.js
@@ -0,0 +1,24 @@
+import React from 'react';
+import ShortAnswer from './ShortAnswer.js';
+import LongAnswer from './LongAnswer.js';
+import MultipleChoice from './MultipleChoice.js';
+import SingleChoice from './singleChoice.js';
+
+var Question = React.createClass({
+ render: function() {
+ var answerTypes = {
+ short_answer: ShortAnswer,
+ long_answer: LongAnswer,
+ single_choice: SingleChoice,
+ multiple_choice: MultipleChoice
+ };
+ if (this.props.type) {
+ return answerTypes[this.props.type](this.props);
+ } else {
+ return answerTypes['short_answer'](this.props);
+ }
+
+ }
+});
+
+export default Question;
diff --git a/public/scripts/components/QuestionForm.js b/public/scripts/components/QuestionForm.js
new file mode 100644
index 00000000..75953807
--- /dev/null
+++ b/public/scripts/components/QuestionForm.js
@@ -0,0 +1,89 @@
+import React from 'react';
+import ChoiceInput from './ChoiceInput';
+import QuestionPreview from './QuestionPreview';
+var QuestionForm = React.createClass({
+ getInitialState: function() {
+ return {question: {}};
+ },
+ handleLabelChange: function(event) {
+ this.state.question.label = event.target.value;
+ this.setState({question: this.state.question});
+ },
+ handleTypeChange: function(e) {
+ this.state.question.type = e.target.value;
+
+ if (e.target.value === "single_choice" || e.target.value === "multiple_choice") {
+ this.state.question.choices = [{
+ id:0,
+ text:""
+ }]
+ }
+
+ this.setState({question:this.state.question});
+ },
+ handleChoices: function(choiceValue, id){
+ var choices = this.state.question.choices.map(function(choice){
+ if (choice.id === id) {
+ return {id: id, text:choiceValue};
+ } else {
+ return choice
+ }
+ });
+ this.state.question.choices = choices;
+ this.setState({question: this.state.question});
+ },
+ addChoice: function() {
+ var choiceId = this.state.question.choices.length;
+ this.state.question.choices.push({
+ id:choiceId,
+ text:""
+ });
+ this.setState({question: this.state.question})
+ },
+ removeChoice: function(id) {
+ var choices = this.state.question.choices.filter(function(choice){
+ return choice.id !== id;
+ });
+ this.state.question.choices = choices;
+ this.setState({question: this.state.question});
+ },
+ handleSubmit: function(e) {
+ e.preventDefault();
+ this.props.QuestionSubmit(this.state.question);
+ e.target.reset();
+ this.setState({question:{}});
+ },
+
+ render: function() {
+ var choices = (this.state.question.type === "single_choice" || this.state.question.type === "multiple_choice");
+ return (
+
+ );
+ }
+});
+export default QuestionForm;
diff --git a/public/scripts/components/QuestionList.js b/public/scripts/components/QuestionList.js
new file mode 100644
index 00000000..0ea3b082
--- /dev/null
+++ b/public/scripts/components/QuestionList.js
@@ -0,0 +1,31 @@
+import React from 'react';
+import Question from './Question.js';
+
+var QuestionList = React.createClass({
+ removeQuestion: function(event, id) {
+ this.props.removeQuestion(id);
+ },
+ render: function() {
+ var questionNodes = this.props.data.map(function(question) {
+ return (
+ this.removeQuestion(e, question.id)}
+ >
+
+ );
+
+ }, this);
+ return (
+
+ {questionNodes}
+
+ );
+ }
+});
+
+export default QuestionList;
diff --git a/public/scripts/components/QuestionPreview.js b/public/scripts/components/QuestionPreview.js
new file mode 100644
index 00000000..82ad2834
--- /dev/null
+++ b/public/scripts/components/QuestionPreview.js
@@ -0,0 +1,27 @@
+import React from 'react';
+import Question from './Question.js';
+var QuestionPreview = React.createClass({
+ render: function() {
+
+ return (
+
+
Question Preview
+
+
+
+
+ );
+ }
+});
+
+export default QuestionPreview;
+
+var divStyle = {
+ marginTop: 20,
+ padding:10,
+ border: "1px dashed #333"
+};
diff --git a/public/scripts/components/ShortAnswer.js b/public/scripts/components/ShortAnswer.js
new file mode 100644
index 00000000..175490d7
--- /dev/null
+++ b/public/scripts/components/ShortAnswer.js
@@ -0,0 +1,17 @@
+import React from 'react';
+var ShortAnswer = function(props) {
+ return(
+
+
+ {props.label}
+
+
+ { props.id &&
+ x
+ }
+
+ );
+
+};
+
+export default ShortAnswer;
diff --git a/public/scripts/components/singleChoice.js b/public/scripts/components/singleChoice.js
new file mode 100644
index 00000000..37dfc232
--- /dev/null
+++ b/public/scripts/components/singleChoice.js
@@ -0,0 +1,26 @@
+import React from 'react';
+import Choice from './Choice.js';
+var SingleChoice = function(props) {
+ var choiceNodes = props.choices.map(function(choice) {
+ return (
+
+
+ );
+ });
+ return (
+
+
+
+ {props.label}
+
+ {choiceNodes}
+
+ { props.id &&
+ x
+ }
+
+ );
+
+};
+
+export default SingleChoice;
diff --git a/public/scripts/example.js b/public/scripts/example.js
deleted file mode 100644
index c249427a..00000000
--- a/public/scripts/example.js
+++ /dev/null
@@ -1,146 +0,0 @@
-/**
- * This file provided by Facebook is for non-commercial testing and evaluation
- * purposes only. Facebook reserves all rights not expressly granted.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
- * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
-
-var Comment = React.createClass({
- rawMarkup: function() {
- var rawMarkup = marked(this.props.children.toString(), {sanitize: true});
- return { __html: rawMarkup };
- },
-
- render: function() {
- return (
-
-
- {this.props.author}
-
-
-
- );
- }
-});
-
-var CommentBox = React.createClass({
- loadCommentsFromServer: function() {
- $.ajax({
- url: this.props.url,
- dataType: 'json',
- cache: false,
- success: function(data) {
- this.setState({data: data});
- }.bind(this),
- error: function(xhr, status, err) {
- console.error(this.props.url, status, err.toString());
- }.bind(this)
- });
- },
- handleCommentSubmit: function(comment) {
- var comments = this.state.data;
- // Optimistically set an id on the new comment. It will be replaced by an
- // id generated by the server. In a production application you would likely
- // not use Date.now() for this and would have a more robust system in place.
- comment.id = Date.now();
- var newComments = comments.concat([comment]);
- this.setState({data: newComments});
- $.ajax({
- url: this.props.url,
- dataType: 'json',
- type: 'POST',
- data: comment,
- success: function(data) {
- this.setState({data: data});
- }.bind(this),
- error: function(xhr, status, err) {
- this.setState({data: comments});
- console.error(this.props.url, status, err.toString());
- }.bind(this)
- });
- },
- getInitialState: function() {
- return {data: []};
- },
- componentDidMount: function() {
- this.loadCommentsFromServer();
- setInterval(this.loadCommentsFromServer, this.props.pollInterval);
- },
- render: function() {
- return (
-
-
Comments
-
-
-
- );
- }
-});
-
-var CommentList = React.createClass({
- render: function() {
- var commentNodes = this.props.data.map(function(comment) {
- return (
-
- {comment.text}
-
- );
- });
- return (
-
- {commentNodes}
-
- );
- }
-});
-
-var CommentForm = React.createClass({
- getInitialState: function() {
- return {author: '', text: ''};
- },
- handleAuthorChange: function(e) {
- this.setState({author: e.target.value});
- },
- handleTextChange: function(e) {
- this.setState({text: e.target.value});
- },
- handleSubmit: function(e) {
- e.preventDefault();
- var author = this.state.author.trim();
- var text = this.state.text.trim();
- if (!text || !author) {
- return;
- }
- this.props.onCommentSubmit({author: author, text: text});
- this.setState({author: '', text: ''});
- },
- render: function() {
- return (
-
- );
- }
-});
-
-ReactDOM.render(
- ,
- document.getElementById('content')
-);
diff --git a/public/scripts/questions.js b/public/scripts/questions.js
new file mode 100644
index 00000000..c614c591
--- /dev/null
+++ b/public/scripts/questions.js
@@ -0,0 +1,84 @@
+import React from 'react';
+import ReactDOM from 'react-dom';
+import QuestionList from './components/QuestionList.js';
+import QuestionForm from './components/QuestionForm.js';
+
+var App = React.createClass({
+ loadQuestionsFromServer: function() {
+ var self = this;
+ $.ajax({
+ url: this.props.url,
+ dataType: 'json',
+ }).done(
+ function(data){
+ self.setState({data: data});
+ }
+ ).fail(
+ function(xhr, status, err) {
+ console.error(this.props.url, status, err.toString());
+ }
+ );
+ },
+ handleQuestionSubmit: function(question) {
+ //give the question an id
+ question.id = Date.now();
+
+ $.ajax({
+ url: this.props.url,
+ dataType: 'json',
+ type: 'POST',
+ data: question
+ }).done(
+ function(data) {
+ this.setState({data:data})
+ }
+ ).fail(
+ function(xhr, status, err) {
+ console.error(this.props.url, status, err.toString());
+ }
+ );
+ },
+ getInitialState: function() {
+ return { data: [] };
+ },
+ componentDidMount: function() {
+ this.loadQuestionsFromServer();
+ },
+ removeQuestion: function(id) {
+ var self = this;
+ if(confirm('Are you sure you want to delete this question?')) {
+ $.ajax({
+ url: this.props.url + "/" + id,
+ type: "DELETE"
+ }).done(
+ function(data) {
+ self.setState( { data:data } );
+ }
+ ).fail(
+ function(xhr, status, err) {
+ console.error( this.props.url, status, err.toString() );
+ }
+ );
+ }
+ },
+ render: function() {
+ return (
+
+
+
+
+
+
Questions
+
+
+
+ );
+ }
+});
+
+ReactDOM.render(
+ ,
+ document.getElementById('content')
+);
diff --git a/questions.json b/questions.json
new file mode 100644
index 00000000..3f227606
--- /dev/null
+++ b/questions.json
@@ -0,0 +1,131 @@
+[
+ {
+ "id": 1456000625379,
+ "type": "multiple_choice",
+ "label": "Test Choose Multi",
+ "choices": [
+ {
+ "id": "0",
+ "text": "Choice 1"
+ },
+ {
+ "id": "1",
+ "text": "Choice 2"
+ },
+ {
+ "id": "2",
+ "text": "Choice 3"
+ }
+ ]
+ },
+ {
+ "id": 1456003359026,
+ "type": "single_choice",
+ "label": "Choose 1",
+ "choices": [
+ {
+ "id": "0",
+ "text": "Choice 1"
+ },
+ {
+ "id": "2",
+ "text": "Choice 3"
+ }
+ ]
+ },
+ {
+ "id": 1456003745055,
+ "type": "multiple_choice",
+ "label": "Test",
+ "choices": [
+ {
+ "id": "0",
+ "text": "test 1"
+ },
+ {
+ "id": "1",
+ "text": "test 2"
+ },
+ {
+ "id": "2",
+ "text": "test 3"
+ }
+ ]
+ },
+ {
+ "id": 1456004085566,
+ "type": "single_choice",
+ "label": "test",
+ "choices": [
+ {
+ "id": "0",
+ "text": "You only get one"
+ }
+ ]
+ },
+ {
+ "id": 1456004137688,
+ "type": "single_choice",
+ "label": "Choose 1",
+ "choices": [
+ {
+ "id": "0",
+ "text": "Let's try again!"
+ }
+ ]
+ },
+ {
+ "id": 1456004220764,
+ "type": "single_choice",
+ "label": "which one ",
+ "choices": [
+ {
+ "id": "0",
+ "text": "this one"
+ },
+ {
+ "id": "1",
+ "text": "that one"
+ }
+ ]
+ },
+ {
+ "id": 1456006185410,
+ "type": "long_answer",
+ "label": "New long Question"
+ },
+ {
+ "id": 1456006291834,
+ "type": "single_choice",
+ "label": "New Choosy Question",
+ "choices": [
+ {
+ "id": "0",
+ "text": "Question 1"
+ },
+ {
+ "id": "1",
+ "text": "Question 2"
+ }
+ ]
+ },
+ {
+ "id": 1456006339898,
+ "type": "multiple_choice",
+ "label": "New Multi Question",
+ "choices": [
+ {
+ "id": "0",
+ "text": "Choice 1"
+ },
+ {
+ "id": "1",
+ "text": "Choice 2"
+ },
+ {
+ "id": "2",
+ "text": "Choice 3"
+ }
+ ]
+ }
+]
\ No newline at end of file
diff --git a/requirements.txt b/requirements.txt
deleted file mode 100644
index 632a1efa..00000000
--- a/requirements.txt
+++ /dev/null
@@ -1 +0,0 @@
-Flask==0.10.1
diff --git a/server.go b/server.go
deleted file mode 100644
index 2224328d..00000000
--- a/server.go
+++ /dev/null
@@ -1,110 +0,0 @@
-/**
- * This file provided by Facebook is for non-commercial testing and evaluation
- * purposes only. Facebook reserves all rights not expressly granted.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
- * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
-
-package main
-
-import (
- "bytes"
- "encoding/json"
- "fmt"
- "io"
- "io/ioutil"
- "log"
- "net/http"
- "os"
- "sync"
- "time"
-)
-
-type comment struct {
- ID int64 `json:"id"`
- Author string `json:"author"`
- Text string `json:"text"`
-}
-
-const dataFile = "./comments.json"
-
-var commentMutex = new(sync.Mutex)
-
-// Handle comments
-func handleComments(w http.ResponseWriter, r *http.Request) {
- // Since multiple requests could come in at once, ensure we have a lock
- // around all file operations
- commentMutex.Lock()
- defer commentMutex.Unlock()
-
- // Stat the file, so we can find its current permissions
- fi, err := os.Stat(dataFile)
- if err != nil {
- http.Error(w, fmt.Sprintf("Unable to stat the data file (%s): %s", dataFile, err), http.StatusInternalServerError)
- return
- }
-
- // Read the comments from the file.
- commentData, err := ioutil.ReadFile(dataFile)
- if err != nil {
- http.Error(w, fmt.Sprintf("Unable to read the data file (%s): %s", dataFile, err), http.StatusInternalServerError)
- return
- }
-
- switch r.Method {
- case "POST":
- // Decode the JSON data
- var comments []comment
- if err := json.Unmarshal(commentData, &comments); err != nil {
- http.Error(w, fmt.Sprintf("Unable to Unmarshal comments from data file (%s): %s", dataFile, err), http.StatusInternalServerError)
- return
- }
-
- // Add a new comment to the in memory slice of comments
- comments = append(comments, comment{ID: time.Now().UnixNano() / 1000000, Author: r.FormValue("author"), Text: r.FormValue("text")})
-
- // Marshal the comments to indented json.
- commentData, err = json.MarshalIndent(comments, "", " ")
- if err != nil {
- http.Error(w, fmt.Sprintf("Unable to marshal comments to json: %s", err), http.StatusInternalServerError)
- return
- }
-
- // Write out the comments to the file, preserving permissions
- err := ioutil.WriteFile(dataFile, commentData, fi.Mode())
- if err != nil {
- http.Error(w, fmt.Sprintf("Unable to write comments to data file (%s): %s", dataFile, err), http.StatusInternalServerError)
- return
- }
-
- w.Header().Set("Content-Type", "application/json")
- w.Header().Set("Cache-Control", "no-cache")
- io.Copy(w, bytes.NewReader(commentData))
-
- case "GET":
- w.Header().Set("Content-Type", "application/json")
- w.Header().Set("Cache-Control", "no-cache")
- // stream the contents of the file to the response
- io.Copy(w, bytes.NewReader(commentData))
-
- default:
- // Don't know the method, so error
- http.Error(w, fmt.Sprintf("Unsupported method: %s", r.Method), http.StatusMethodNotAllowed)
- }
-}
-
-func main() {
- port := os.Getenv("PORT")
- if port == "" {
- port = "3000"
- }
- http.HandleFunc("/api/comments", handleComments)
- http.Handle("/", http.FileServer(http.Dir("./public")))
- log.Println("Server started: http://localhost:" + port)
- log.Fatal(http.ListenAndServe(":"+port, nil))
-}
diff --git a/server.js b/server.js
index ac87898a..5806644f 100644
--- a/server.js
+++ b/server.js
@@ -1,14 +1,3 @@
-/**
- * This file provided by Facebook is for non-commercial testing and evaluation
- * purposes only. Facebook reserves all rights not expressly granted.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
- * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
var fs = require('fs');
var path = require('path');
@@ -16,7 +5,7 @@ var express = require('express');
var bodyParser = require('body-parser');
var app = express();
-var COMMENTS_FILE = path.join(__dirname, 'comments.json');
+var QUESTIONS_FILE = path.join(__dirname, 'questions.json');
app.set('port', (process.env.PORT || 3000));
@@ -24,8 +13,8 @@ app.use('/', express.static(path.join(__dirname, 'public')));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: true}));
-app.get('/api/comments', function(req, res) {
- fs.readFile(COMMENTS_FILE, function(err, data) {
+app.get('/api/questions', function(req, res) {
+ fs.readFile(QUESTIONS_FILE, function(err, data) {
if (err) {
console.error(err);
process.exit(1);
@@ -35,29 +24,52 @@ app.get('/api/comments', function(req, res) {
});
});
-app.post('/api/comments', function(req, res) {
- fs.readFile(COMMENTS_FILE, function(err, data) {
+app.post('/api/questions/', function(req, res) {
+ fs.readFile(QUESTIONS_FILE, function(err, data) {
if (err) {
console.error(err);
process.exit(1);
}
- var comments = JSON.parse(data);
- // NOTE: In a real implementation, we would likely rely on a database or
- // some other approach (e.g. UUIDs) to ensure a globally unique id. We'll
- // treat Date.now() as unique-enough for our purposes.
- var newComment = {
+ var questions = JSON.parse(data);
+
+ var newQuestion = {
id: Date.now(),
- author: req.body.author,
- text: req.body.text,
+ type: req.body.type,
+ label: req.body.label,
+ choices: req.body.choices
};
- comments.push(newComment);
- fs.writeFile(COMMENTS_FILE, JSON.stringify(comments, null, 4), function(err) {
+ questions.push(newQuestion);
+ fs.writeFile(QUESTIONS_FILE, JSON.stringify(questions, null, 4), function(err) {
+ if (err) {
+ console.error(err);
+ process.exit(1);
+ }
+ res.setHeader('Cache-Control', 'no-cache');
+ res.json(questions);
+ });
+ });
+});
+
+app.delete('/api/questions/:id', function(req, res){
+ fs.readFile(QUESTIONS_FILE, function(err, data) {
+ if (err) {
+ console.error(err);
+ process.exit(1);
+ }
+ var questions = JSON.parse(data);
+ var id = req.params.id;
+
+ var newQuestions = questions.filter(function(question){
+ return question.id !== parseInt(id);
+ });
+
+ fs.writeFile(QUESTIONS_FILE, JSON.stringify(newQuestions, null, 4), function(err) {
if (err) {
console.error(err);
process.exit(1);
}
res.setHeader('Cache-Control', 'no-cache');
- res.json(comments);
+ res.json(newQuestions);
});
});
});
diff --git a/server.php b/server.php
deleted file mode 100644
index 6b8880c8..00000000
--- a/server.php
+++ /dev/null
@@ -1,52 +0,0 @@
- round(microtime(true) * 1000),
- 'author' => $_POST['author'],
- 'text' => $_POST['text']
- ];
-
- $comments = json_encode($commentsDecoded, JSON_PRETTY_PRINT);
- file_put_contents('comments.json', $comments);
- }
- header('Content-Type: application/json');
- header('Cache-Control: no-cache');
- echo $comments;
- } else {
- return false;
- }
-}
diff --git a/server.pl b/server.pl
deleted file mode 100644
index 517e1621..00000000
--- a/server.pl
+++ /dev/null
@@ -1,36 +0,0 @@
-# This file provided by Facebook is for non-commercial testing and evaluation
-# purposes only. Facebook reserves all rights not expressly granted.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-# FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
-# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-use Time::HiRes qw(gettimeofday);
-use Mojolicious::Lite;
-use Mojo::JSON qw(encode_json decode_json);
-
-app->static->paths->[0] = './public';
-
-any '/' => sub { $_[0]->reply->static('index.html') };
-
-any [qw(GET POST)] => '/api/comments' => sub {
- my $self = shift;
- my $comments = decode_json (do { local(@ARGV,$/) = 'comments.json';<> });
-
- if ($self->req->method eq 'POST')
- {
- push @$comments, {
- id => int(gettimeofday * 1000),
- author => $self->param('author'),
- text => $self->param('text'),
- };
- open my $FILE, '>', 'comments.json';
- print $FILE encode_json($comments);
- }
- $self->render(json => $comments);
-};
-my $port = $ENV{PORT} || 3000;
-app->start('daemon', '-l', "http://*:$port");
diff --git a/server.py b/server.py
deleted file mode 100644
index 451fbacd..00000000
--- a/server.py
+++ /dev/null
@@ -1,36 +0,0 @@
-# This file provided by Facebook is for non-commercial testing and evaluation
-# purposes only. Facebook reserves all rights not expressly granted.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-# FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
-# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-import json
-import os
-import time
-from flask import Flask, Response, request
-
-app = Flask(__name__, static_url_path='', static_folder='public')
-app.add_url_rule('/', 'root', lambda: app.send_static_file('index.html'))
-
-@app.route('/api/comments', methods=['GET', 'POST'])
-def comments_handler():
-
- with open('comments.json', 'r') as file:
- comments = json.loads(file.read())
-
- if request.method == 'POST':
- newComment = request.form.to_dict()
- newComment['id'] = int(time.time() * 1000)
- comments.append(newComment)
-
- with open('comments.json', 'w') as file:
- file.write(json.dumps(comments, indent=4, separators=(',', ': ')))
-
- return Response(json.dumps(comments), mimetype='application/json', headers={'Cache-Control': 'no-cache'})
-
-if __name__ == '__main__':
- app.run(port=int(os.environ.get("PORT",3000)))
diff --git a/server.rb b/server.rb
deleted file mode 100644
index eed401ae..00000000
--- a/server.rb
+++ /dev/null
@@ -1,46 +0,0 @@
-# This file provided by Facebook is for non-commercial testing and evaluation
-# purposes only. Facebook reserves all rights not expressly granted.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-# FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
-# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-require 'webrick'
-require 'json'
-
-port = ENV['PORT'].nil? ? 3000 : ENV['PORT'].to_i
-
-puts "Server started: http://localhost:#{port}/"
-
-root = File.expand_path './public'
-server = WEBrick::HTTPServer.new Port: port, DocumentRoot: root
-
-server.mount_proc '/api/comments' do |req, res|
- comments = JSON.parse(File.read('./comments.json', encoding: 'UTF-8'))
-
- if req.request_method == 'POST'
- # Assume it's well formed
- comment = { id: (Time.now.to_f * 1000).to_i }
- req.query.each do |key, value|
- comment[key] = value.force_encoding('UTF-8')
- end
- comments << comment
- File.write(
- './comments.json',
- JSON.pretty_generate(comments, indent: ' '),
- encoding: 'UTF-8'
- )
- end
-
- # always return json
- res['Content-Type'] = 'application/json'
- res['Cache-Control'] = 'no-cache'
- res.body = JSON.generate(comments)
-end
-
-trap('INT') { server.shutdown }
-
-server.start
diff --git a/webpack.config.js b/webpack.config.js
new file mode 100644
index 00000000..a14b1118
--- /dev/null
+++ b/webpack.config.js
@@ -0,0 +1,32 @@
+var path = require('path');
+var webpack = require('webpack');
+module.exports = {
+ entry: './public/scripts/questions.js',
+ output: {
+ path: __dirname + "/public",
+ filename: 'bundle.js'
+ },
+ module: {
+ loaders: [
+ {
+ test: /\.js?$/,
+ include: [
+ path.resolve(__dirname, "public/scripts")
+ ],
+ loader: 'babel-loader',
+ query: {
+ presets: ['es2015', 'react']
+ }
+
+ }
+ //{ test: /\.less$/, loader: 'style-loader!css-loader!less-loader' }, // use ! to chain loaders
+ //{ test: /\.css$/, loader: 'style-loader!css-loader' },
+ //{ test: /\.(png|jpg)$/, loader: 'url-loader?limit=8192' } // inline base64 URLs for <=8k images, direct URLs for the rest
+ ]
+ },
+ plugins: [
+ new webpack.ProvidePlugin({
+ $: "jquery",
+ })
+ ]
+};