diff --git a/src/main/resources/static/css/comments.css b/src/main/resources/static/css/comments.css
new file mode 100644
index 0000000..3578e80
--- /dev/null
+++ b/src/main/resources/static/css/comments.css
@@ -0,0 +1,12 @@
+#content {
+ margin-top: 60px;
+}
+
+.footer {
+ position: absolute;
+ bottom: 0;
+ width: 100%;
+ height: 60px;
+ background-color: #101010;
+ color: #9d9d9d;
+}
\ No newline at end of file
diff --git a/src/main/resources/static/js/app.jsx b/src/main/resources/static/js/app.jsx
new file mode 100644
index 0000000..f4dc3da
--- /dev/null
+++ b/src/main/resources/static/js/app.jsx
@@ -0,0 +1,123 @@
+'use strict';
+
+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 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 (
+
+ );
+ }
+});
+var CommentBox = React.createClass({
+ getInitialState: function() {
+ return {data: []};
+ },
+ 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;
+ comment.id = Date.now();
+ var newComments = comments.concat([comment]);
+ this.setState({data: newComments});
+ $.ajax({
+ url: this.props.url,
+ method: 'POST',
+ dataType: 'json',
+ 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)
+ });
+ },
+ 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}
+
+ )
+ }
+});
\ No newline at end of file
diff --git a/src/main/resources/static/js/app.render.jsx b/src/main/resources/static/js/app.render.jsx
new file mode 100644
index 0000000..9709ace
--- /dev/null
+++ b/src/main/resources/static/js/app.render.jsx
@@ -0,0 +1,4 @@
+ReactDOM.render(
+ ,
+ document.getElementById('content')
+);
\ No newline at end of file