diff --git a/frontendJS/universal-react/.babelrc b/frontendJS/universal-react/.babelrc
new file mode 100644
index 0000000..203e642
--- /dev/null
+++ b/frontendJS/universal-react/.babelrc
@@ -0,0 +1,3 @@
+{
+ "presets": ["react", "es2015"]
+}
\ No newline at end of file
diff --git a/frontendJS/universal-react/.editorconfig b/frontendJS/universal-react/.editorconfig
new file mode 100644
index 0000000..b909682
--- /dev/null
+++ b/frontendJS/universal-react/.editorconfig
@@ -0,0 +1,20 @@
+# EditorConfig is awesome: http://EditorConfig.org
+
+# top-most EditorConfig file
+root = true
+
+# Unix-style newlines with a newline ending every file
+[*]
+end_of_line = lf
+insert_final_newline = false
+charset = utf-8
+indent_style = tab
+trim_trailing_whitespace = true
+
+[*.{cpp,c,h,hpp,cxx}]
+insert_final_newline = true
+
+# Yaml files
+[*.{yml,yaml}]
+indent_style = space
+indent_size = 4
\ No newline at end of file
diff --git a/frontendJS/universal-react/.gitignore b/frontendJS/universal-react/.gitignore
new file mode 100644
index 0000000..be65ac9
--- /dev/null
+++ b/frontendJS/universal-react/.gitignore
@@ -0,0 +1,4 @@
+dist/
+babel_cache/
+node_modules/
+public/js/bundle.js
diff --git a/frontendJS/universal-react/package.json b/frontendJS/universal-react/package.json
new file mode 100644
index 0000000..b126314
--- /dev/null
+++ b/frontendJS/universal-react/package.json
@@ -0,0 +1,27 @@
+{
+ "dependencies": {
+ "babel-cli": "^6.11.4",
+ "babel-core": "^6.13.2",
+ "babel-preset-es2015": "^6.13.2",
+ "babel-preset-react": "^6.11.1",
+ "ejs": "^2.5.1",
+ "express": "^4.14.0",
+ "react": "^15.3.1",
+ "react-dom": "^15.3.1",
+ "react-router": "^2.6.1"
+ },
+ "devDependencies": {
+ "babel-loader": "^6.2.5",
+ "http-server": "^0.9.0",
+ "webpack": "^1.13.2"
+ },
+ "scripts": {
+ "start": "NODE_ENV=production babel-node src/server.js",
+ "build": "npm run build:client && npm run build:server",
+ "build:client": "NODE_ENV=production webpack -p",
+ "build:server": "NODE_ENV=production babel src -d dist",
+ "buildrun": "npm run build && npm run serve",
+ "clean": "rm -rf ./dist && rm -rf ./babel_cache && rm ./public/js/bundle.js",
+ "serve": "node dist/server.js"
+ }
+}
diff --git a/frontendJS/universal-react/public/css/style.css b/frontendJS/universal-react/public/css/style.css
new file mode 100644
index 0000000..83e29e2
--- /dev/null
+++ b/frontendJS/universal-react/public/css/style.css
@@ -0,0 +1,246 @@
+/*! CSS reset from benfrain/app-reset */
+*,:after,:before{box-sizing:inherit}html{box-sizing:border-box}
+*{user-select:none;-webkit-tap-highlight-color:rgba(255,255,255,0);-webkit-tap-highlight-color:transparent}
+[contenteditable],input[type]{user-select:text}body,h1,h2,h3,h4,h5,h6,p{margin:0;font-size:1rem;font-weight:400}
+a{text-decoration:none;color:inherit}b{font-weight:400}em,i{font-style:normal}a:focus,input:focus{outline:0}
+fieldset,input{appearance:none;border:0;padding:0;margin:0;min-width:0;font-size:1rem;font-family:inherit}
+input::-ms-clear{display:none}input[type=number]{-moz-appearance:textfield}
+input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{appearance:none}
+svg{display:inline-flex}img{max-width:100%;display:block}
+
+html {
+ color: #030303;
+ font: caption;
+ padding-bottom: 2em;
+}
+
+a {
+ color: #1f4ba0;
+ text-decoration: underline;
+}
+
+header {
+ padding: 2em;
+}
+
+header .logo {
+ display: block;
+ margin: 0 auto;
+ max-width: 400px;
+ width: 100%;
+}
+
+footer {
+ margin: 4em auto;
+ padding: 2em;
+ text-align: center;
+ max-width: 800px;
+ width: 100%;
+}
+
+footer p {
+ line-height: 1.2em;
+ margin: 0 0 1em 0;
+}
+
+.not-found {
+ margin: 0 auto;
+ max-width: 800px;
+ padding: 2em;
+ text-align: center;
+ width: 100%;
+}
+
+.not-found h1 {
+ font-size: 3em;
+ font-weight: bold;
+}
+
+.not-found h2 {
+ font-size: 2em;
+ font-weight: bold;
+}
+
+.home .athletes-selector {
+ clear: both;
+ padding: 2em;
+ text-align: center;
+}
+
+.home .athletes-selector .athlete-preview {
+ border: 1px solid #ccc;
+ box-shadow: 0 2px 5px 0 rgba(0,0,0,0.16),0 2px 10px 0 rgba(0,0,0,0.12);
+ display: inline-block;
+ margin: 0 1em 1em 0;
+ max-width: 200px;
+ padding: 0;
+ position: relative;
+ width: 100%;
+}
+
+.home .athletes-selector .athlete-preview img {
+ margin: 0;
+ width: 100%;
+}
+
+.home .athletes-selector .athlete-preview .name {
+ color: #030303;
+ display: inline-block;
+ font-size: 1.6em;
+ overflow: hidden;
+ padding: .2em;
+ text-align: center;
+ text-decoration: none;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ width: 100%;
+}
+
+.home .athletes-selector .athlete-preview .medals-count {
+ background: #fff;
+ border-radius: 2px;
+ display: inline-block;
+ font-weight: bold;
+ margin: .2em;
+ padding: .2em .4em;
+ position: absolute;
+ right: 0;
+ text-align: center;
+ top: 0;
+ vertical-align: middle;
+}
+
+nav.atheletes-menu {
+ margin: 0 auto;
+ max-width: 800px;
+ padding: 2em;
+ text-align: center;
+ width: 100%;
+}
+
+nav.atheletes-menu a {
+ font-size: 1.6em;
+ margin: 0 1em 1em 0;
+}
+
+nav.atheletes-menu a.active {
+ color: #030303;
+ text-decoration: none;
+}
+
+.athlete {
+ border: 1px solid #ccc;
+ box-shadow: 0 2px 5px 0 rgba(0,0,0,0.16),0 2px 10px 0 rgba(0,0,0,0.12);
+ margin: 0 auto;
+ max-width: 800px;
+ width: 100%;
+}
+
+.athlete header {
+ background: #ccc;
+ background-position: center center;
+ background-repeat: no-repeat;
+ background-size: cover;
+ width: 100%;
+ height: 200px;
+}
+
+.athlete .picture-container {
+ margin: -160px 0 0 0;
+ padding: 0 1em 0 1em;
+}
+
+.athlete .picture-container img {
+ border-radius: 4px;
+ border: 8px solid #fff;
+ box-shadow: 0 2px 5px 0 rgba(0,0,0,0.16),0 2px 10px 0 rgba(0,0,0,0.12);
+ display: inline;
+ margin: 0 0 0 1em;
+ width: 200px;
+}
+
+.athlete .picture-container h2 {
+ display: inline;
+ font-size: 3em;
+ padding: 0 0 0 .5em;
+ width: auto;
+}
+
+.athlete .flag .icon {
+ display: inline-block;
+ padding-bottom: .1em;
+ width: auto;
+}
+
+.athlete .description,
+.athlete .medals {
+ font-size: 1.6em;
+ padding: 1em;
+}
+
+.athlete .medals p {
+ font-size: 1em;
+}
+
+.athlete .medals li {
+ list-style: none;
+}
+
+.athlete .medals .symbol {
+ border-radius: 50%;
+ display: inline-block;
+ font-size: .8em;
+ height: 1.6em;
+ justify-content: center;
+ margin: 5px;
+ text-align: center;
+ width: 1.6em;
+}
+
+.athlete .medals .symbol.symbol-G {
+ color: #daa520;
+ background-color: #fff6de;
+ border: 2px solid #daa520;
+}
+
+.athlete .medals .symbol.symbol-S {
+ color: #383738;
+ background-color: #b9b5b5;
+ border: 2px solid #383738;
+}
+
+.athlete .medals .symbol.symbol-B {
+ color: #6b1919;
+ background-color: #ea96a1;
+ border: 2px solid #6E1924;
+}
+
+.navigateBack {
+ font-size: 1.6em;
+ padding: 2em;
+ text-align: center;
+ text-decoration: none;
+}
+
+#abar {
+ bottom: 0;
+ left: 0;
+ position: fixed;
+ width: 100%;
+}
+
+#abar a {
+ background: #ffa500;
+ color: #000;
+ display: block;
+ font-weight: bold;
+ margin: 0;
+ padding: 1em;
+ text-align: center;
+ text-decoration: none;
+ width: 100%;
+}
+
+#abar a:hover {
+ text-decoration: underline;
+}
diff --git a/frontendJS/universal-react/public/favicon.ico b/frontendJS/universal-react/public/favicon.ico
new file mode 100644
index 0000000..c6fc0b4
Binary files /dev/null and b/frontendJS/universal-react/public/favicon.ico differ
diff --git a/frontendJS/universal-react/public/img/driulis-gonzalez-cover.jpg b/frontendJS/universal-react/public/img/driulis-gonzalez-cover.jpg
new file mode 100644
index 0000000..b85f6ab
Binary files /dev/null and b/frontendJS/universal-react/public/img/driulis-gonzalez-cover.jpg differ
diff --git a/frontendJS/universal-react/public/img/driulis-gonzalez.jpg b/frontendJS/universal-react/public/img/driulis-gonzalez.jpg
new file mode 100644
index 0000000..9c8e84d
Binary files /dev/null and b/frontendJS/universal-react/public/img/driulis-gonzalez.jpg differ
diff --git a/frontendJS/universal-react/public/img/flag-cu.png b/frontendJS/universal-react/public/img/flag-cu.png
new file mode 100755
index 0000000..083f1d6
Binary files /dev/null and b/frontendJS/universal-react/public/img/flag-cu.png differ
diff --git a/frontendJS/universal-react/public/img/flag-fr.png b/frontendJS/universal-react/public/img/flag-fr.png
new file mode 100755
index 0000000..8332c4e
Binary files /dev/null and b/frontendJS/universal-react/public/img/flag-fr.png differ
diff --git a/frontendJS/universal-react/public/img/flag-jp.png b/frontendJS/universal-react/public/img/flag-jp.png
new file mode 100755
index 0000000..325fbad
Binary files /dev/null and b/frontendJS/universal-react/public/img/flag-jp.png differ
diff --git a/frontendJS/universal-react/public/img/flag-nl.png b/frontendJS/universal-react/public/img/flag-nl.png
new file mode 100755
index 0000000..fe44791
Binary files /dev/null and b/frontendJS/universal-react/public/img/flag-nl.png differ
diff --git a/frontendJS/universal-react/public/img/flag-uz.png b/frontendJS/universal-react/public/img/flag-uz.png
new file mode 100755
index 0000000..fef5dc1
Binary files /dev/null and b/frontendJS/universal-react/public/img/flag-uz.png differ
diff --git a/frontendJS/universal-react/public/img/logo-judo-heroes.png b/frontendJS/universal-react/public/img/logo-judo-heroes.png
new file mode 100644
index 0000000..836d97f
Binary files /dev/null and b/frontendJS/universal-react/public/img/logo-judo-heroes.png differ
diff --git a/frontendJS/universal-react/public/img/mark-huizinga-cover.jpg b/frontendJS/universal-react/public/img/mark-huizinga-cover.jpg
new file mode 100644
index 0000000..0b0c026
Binary files /dev/null and b/frontendJS/universal-react/public/img/mark-huizinga-cover.jpg differ
diff --git a/frontendJS/universal-react/public/img/mark-huizinga.jpg b/frontendJS/universal-react/public/img/mark-huizinga.jpg
new file mode 100644
index 0000000..c47a9ec
Binary files /dev/null and b/frontendJS/universal-react/public/img/mark-huizinga.jpg differ
diff --git a/frontendJS/universal-react/public/img/medal.png b/frontendJS/universal-react/public/img/medal.png
new file mode 100644
index 0000000..ef1b08b
Binary files /dev/null and b/frontendJS/universal-react/public/img/medal.png differ
diff --git a/frontendJS/universal-react/public/img/rishod-sobirov-cover.jpg b/frontendJS/universal-react/public/img/rishod-sobirov-cover.jpg
new file mode 100644
index 0000000..64b5c48
Binary files /dev/null and b/frontendJS/universal-react/public/img/rishod-sobirov-cover.jpg differ
diff --git a/frontendJS/universal-react/public/img/rishod-sobirov.jpg b/frontendJS/universal-react/public/img/rishod-sobirov.jpg
new file mode 100644
index 0000000..3f425c5
Binary files /dev/null and b/frontendJS/universal-react/public/img/rishod-sobirov.jpg differ
diff --git a/frontendJS/universal-react/public/img/ryoko-tani-cover.jpg b/frontendJS/universal-react/public/img/ryoko-tani-cover.jpg
new file mode 100644
index 0000000..6b3367a
Binary files /dev/null and b/frontendJS/universal-react/public/img/ryoko-tani-cover.jpg differ
diff --git a/frontendJS/universal-react/public/img/ryoko-tani.jpg b/frontendJS/universal-react/public/img/ryoko-tani.jpg
new file mode 100644
index 0000000..c89d2ed
Binary files /dev/null and b/frontendJS/universal-react/public/img/ryoko-tani.jpg differ
diff --git a/frontendJS/universal-react/public/img/teddy-riner-cover.jpg b/frontendJS/universal-react/public/img/teddy-riner-cover.jpg
new file mode 100644
index 0000000..04784bc
Binary files /dev/null and b/frontendJS/universal-react/public/img/teddy-riner-cover.jpg differ
diff --git a/frontendJS/universal-react/public/img/teddy-riner.jpg b/frontendJS/universal-react/public/img/teddy-riner.jpg
new file mode 100644
index 0000000..c96feb5
Binary files /dev/null and b/frontendJS/universal-react/public/img/teddy-riner.jpg differ
diff --git a/frontendJS/universal-react/public/index.html b/frontendJS/universal-react/public/index.html
new file mode 100644
index 0000000..6042f54
--- /dev/null
+++ b/frontendJS/universal-react/public/index.html
@@ -0,0 +1,13 @@
+
+
+
+
+
+ Judo Heroes - A Universal Javascript demo application with React
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/frontendJS/universal-react/src/app-client.js b/frontendJS/universal-react/src/app-client.js
new file mode 100644
index 0000000..4104fa6
--- /dev/null
+++ b/frontendJS/universal-react/src/app-client.js
@@ -0,0 +1,5 @@
+import React from 'react';
+import ReactDOM from 'react-dom';
+import AppRoutes from './components/AppRoutes';
+
+ReactDOM.render(, document.getElementById('main'));
\ No newline at end of file
diff --git a/frontendJS/universal-react/src/components/AppRoutes.js b/frontendJS/universal-react/src/components/AppRoutes.js
new file mode 100644
index 0000000..3d2f630
--- /dev/null
+++ b/frontendJS/universal-react/src/components/AppRoutes.js
@@ -0,0 +1,13 @@
+import React from 'react';
+import { Router, browserHistory } from 'react-router';
+import routes from '../routes';
+
+class AppRoutes extends React.Component {
+ render() {
+ return (
+ window.scrollTo(0, 0)} />
+ );
+ }
+}
+
+export default AppRoutes;
\ No newline at end of file
diff --git a/frontendJS/universal-react/src/components/AthleteMenu.js b/frontendJS/universal-react/src/components/AthleteMenu.js
new file mode 100644
index 0000000..2b58a57
--- /dev/null
+++ b/frontendJS/universal-react/src/components/AthleteMenu.js
@@ -0,0 +1,19 @@
+import React from 'react';
+import { Link } from 'react-router';
+import athletes from '../data/athletes';
+
+class AthleteMenu extends React.Component {
+ render() {
+ return (
+
+ );
+ }
+}
+
+export default AthleteMenu;
\ No newline at end of file
diff --git a/frontendJS/universal-react/src/components/AthletePage.js b/frontendJS/universal-react/src/components/AthletePage.js
new file mode 100644
index 0000000..aedba3a
--- /dev/null
+++ b/frontendJS/universal-react/src/components/AthletePage.js
@@ -0,0 +1,48 @@
+import React from 'react';
+import { Link } from 'react-router';
+import NotFoundPage from './NotFoundPage';
+import AthleteMenu from './AthleteMenu';
+import Medal from './Medal';
+import Flag from './Flag';
+import athletes from '../data/athletes';
+
+class AthletePage extends React.Component {
+ render() {
+ const id = this.props.params.id;
+ const athlete = athletes.filter(athlete => athlete.id === id)[0];
+ if ( ! athlete) {
+ return
+ }
+ const headerStyle = {
+ backgroundImage: `url(/img/${athlete.cover})`
+ };
+
+ return (
+
+
+
+
+
+
+
{athlete.name}
+
+
+ Olympic medalist from ,
+ born in {athlete.birth} (Find out more on Wikipedia).
+
+
+ Winner of {athlete.medals.length} medals:
+ {
+ athlete.medals.map((medal, i) => )
+ }
+
+
+
+ « Back to the index
+
+
+ );
+ }
+}
+
+export default AthletePage;
\ No newline at end of file
diff --git a/frontendJS/universal-react/src/components/AthletePreview.js b/frontendJS/universal-react/src/components/AthletePreview.js
new file mode 100644
index 0000000..1b933b5
--- /dev/null
+++ b/frontendJS/universal-react/src/components/AthletePreview.js
@@ -0,0 +1,21 @@
+import React from 'react';
+import { Link } from 'react-router';
+
+class AthletePreview extends React.Component {
+ render() {
+ return (
+
+
+
+
{this.props.name}
+
+
+ {this.props.medals.length}
+
+
+
+ );
+ }
+}
+
+export default AthletePreview;
\ No newline at end of file
diff --git a/frontendJS/universal-react/src/components/Flag.js b/frontendJS/universal-react/src/components/Flag.js
new file mode 100644
index 0000000..ae56fdb
--- /dev/null
+++ b/frontendJS/universal-react/src/components/Flag.js
@@ -0,0 +1,40 @@
+import React from 'react';
+
+const data = {
+ cu: {
+ name: 'Cuba',
+ icon: 'flag-cu.png'
+ },
+ fr: {
+ name: 'France',
+ icon: 'flag-fr.png'
+ },
+ jp: {
+ name: 'Japan',
+ icon: 'flag-jp.png'
+ },
+ nl: {
+ name: 'Netherlands',
+ icon: 'flag-nl.png'
+ },
+ uz: {
+ name: 'Uzbekistan',
+ icon: 'flag-uz.png'
+ }
+};
+
+class Flag extends React.Component {
+ render() {
+ const name = data[this.props.code].name;
+ const icon = data[this.props.code].icon;
+
+ return (
+
+
+ {this.props.showName && {name}}
+
+ );
+ }
+}
+
+export default Flag;
\ No newline at end of file
diff --git a/frontendJS/universal-react/src/components/IndexPage.js b/frontendJS/universal-react/src/components/IndexPage.js
new file mode 100644
index 0000000..492e6a5
--- /dev/null
+++ b/frontendJS/universal-react/src/components/IndexPage.js
@@ -0,0 +1,17 @@
+import React from 'react';
+import AthletePreview from './AthletePreview';
+import athletes from '../data/athletes';
+
+class IndexPage extends React.Component {
+ render() {
+ return (
+
+
+ {athletes.map(athleteData =>
)}
+
+
+ );
+ }
+}
+
+export default IndexPage;
\ No newline at end of file
diff --git a/frontendJS/universal-react/src/components/Layout.js b/frontendJS/universal-react/src/components/Layout.js
new file mode 100644
index 0000000..f6fec2c
--- /dev/null
+++ b/frontendJS/universal-react/src/components/Layout.js
@@ -0,0 +1,24 @@
+import React from 'react';
+import { Link } from 'react-router';
+
+class Layout extends React.Component {
+ render() {
+ return (
+
+
+
+
+
+
+
{this.props.children}
+
+
+ );
+ }
+}
+
+export default Layout;
\ No newline at end of file
diff --git a/frontendJS/universal-react/src/components/Medal.js b/frontendJS/universal-react/src/components/Medal.js
new file mode 100644
index 0000000..7c21b38
--- /dev/null
+++ b/frontendJS/universal-react/src/components/Medal.js
@@ -0,0 +1,23 @@
+import React from 'react';
+
+const typeMap = {
+ G: 'Gold',
+ S: 'Silver',
+ B: 'Bronze'
+};
+
+class Medal extends React.Component {
+ render() {
+ return (
+
+ {this.props.type}
+ {this.props.year}
+ {this.props.city}
+ ({this.props.event})
+ {this.props.category}
+
+ );
+ }
+}
+
+export default Medal;
\ No newline at end of file
diff --git a/frontendJS/universal-react/src/components/NotFoundPage.js b/frontendJS/universal-react/src/components/NotFoundPage.js
new file mode 100644
index 0000000..c2b054d
--- /dev/null
+++ b/frontendJS/universal-react/src/components/NotFoundPage.js
@@ -0,0 +1,18 @@
+import React from 'react';
+import { Link } from 'react-router';
+
+class NotFoundPage extends React.Component {
+ render() {
+ return (
+
+
404
+
Page not found!
+
+ Go back to the main page
+
+
+ );
+ }
+}
+
+export default NotFoundPage;
\ No newline at end of file
diff --git a/frontendJS/universal-react/src/data/athletes.js b/frontendJS/universal-react/src/data/athletes.js
new file mode 100644
index 0000000..f773b97
--- /dev/null
+++ b/frontendJS/universal-react/src/data/athletes.js
@@ -0,0 +1,134 @@
+"use strict";
+
+const athletes = [
+ {
+ 'id': 'driulis-gonzalez',
+ 'name': 'Driulis González',
+ 'country': 'cu',
+ 'birth': '1973',
+ 'image': 'driulis-gonzalez.jpg',
+ 'cover': 'driulis-gonzalez-cover.jpg',
+ 'link': 'https://en.wikipedia.org/wiki/Driulis_González',
+ 'medals': [
+ { 'year': '1992', 'type': 'B', 'city': 'Barcelona', 'event': 'Olympic Games', 'category': '-57kg' },
+ { 'year': '1993', 'type': 'B', 'city': 'Hamilton', 'event': 'World Championships', 'category': '-57kg' },
+ { 'year': '1995', 'type': 'G', 'city': 'Chiba', 'event': 'World Championships', 'category': '-57kg' },
+ { 'year': '1995', 'type': 'G', 'city': 'Mar del Plata', 'event': 'Pan American Games', 'category': '-57kg' },
+ { 'year': '1996', 'type': 'G', 'city': 'Atlanta', 'event': 'Olympic Games', 'category': '-57kg' },
+ { 'year': '1997', 'type': 'S', 'city': 'Osaka', 'event': 'World Championships', 'category': '-57kg' },
+ { 'year': '1999', 'type': 'G', 'city': 'Birmingham', 'event': 'World Championships', 'category': '-57kg' },
+ { 'year': '2000', 'type': 'S', 'city': 'Sydney', 'event': 'Olympic Games', 'category': '-57kg' },
+ { 'year': '2003', 'type': 'G', 'city': 'S Domingo', 'event': 'Pan American Games', 'category': '-63kg' },
+ { 'year': '2003', 'type': 'S', 'city': 'Osaka', 'event': 'World Championships', 'category': '-63kg' },
+ { 'year': '2004', 'type': 'B', 'city': 'Athens', 'event': 'Olympic Games', 'category': '-63kg' },
+ { 'year': '2005', 'type': 'B', 'city': 'Cairo', 'event': 'World Championships', 'category': '-63kg' },
+ { 'year': '2006', 'type': 'G', 'city': 'Cartagena', 'event': 'Central American and Caribbean Games', 'category': '-63kg' },
+ { 'year': '2006', 'type': 'G', 'city': 'Cartagena', 'event': 'Central American and Caribbean Games', 'category': 'Tema' },
+ { 'year': '2007', 'type': 'G', 'city': 'Rio de Janeiro', 'event': 'Pan American Games', 'category': '-63kg' },
+ { 'year': '2007', 'type': 'G', 'city': 'Rio de Janeiro', 'event': 'World Championships', 'category': '-63kg' },
+ ],
+ },
+ {
+ 'id': 'mark-huizinga',
+ 'name': 'Mark Huizinga',
+ 'country': 'nl',
+ 'birth': '1973',
+ 'image': 'mark-huizinga.jpg',
+ 'cover': 'mark-huizinga-cover.jpg',
+ 'link': 'https://en.wikipedia.org/wiki/Mark_Huizinga',
+ 'medals': [
+ { 'year': '1994', 'type': 'B', 'city': 'Gdansk', 'event': 'European Championships', 'category': '-78kg' },
+ { 'year': '1996', 'type': 'B', 'city': 'Atlanta', 'event': 'Olympic Games', 'category': '-86kg' },
+ { 'year': '1996', 'type': 'G', 'city': 'The Hague', 'event': 'European Championships', 'category': '-86kg' },
+ { 'year': '1997', 'type': 'G', 'city': 'Oostende', 'event': 'European Championships', 'category': '-86kg' },
+ { 'year': '1998', 'type': 'G', 'city': 'Oviedo', 'event': 'European Championships', 'category': '-90kg' },
+ { 'year': '1999', 'type': 'B', 'city': 'Bratislava', 'event': 'European Championships', 'category': '-90kg' },
+ { 'year': '2000', 'type': 'G', 'city': 'Sydney', 'event': 'Olympic Games', 'category': '-90kg' },
+ { 'year': '2000', 'type': 'S', 'city': 'Wroclaw', 'event': 'European Championships', 'category': '-90kg' },
+ { 'year': '2001', 'type': 'G', 'city': 'Paris', 'event': 'European Championships', 'category': '-90kg' },
+ { 'year': '2002', 'type': 'B', 'city': 'Maribor', 'event': 'European Championships', 'category': '-90kg' },
+ { 'year': '2003', 'type': 'B', 'city': 'Düsseldorf', 'event': 'European Championships', 'category': '-90kg' },
+ { 'year': '2004', 'type': 'B', 'city': 'Athens', 'event': 'Olympic Games', 'category': '-90kg' },
+ { 'year': '2004', 'type': 'S', 'city': 'Bucharest', 'event': 'European Championships', 'category': '-90kg' },
+ { 'year': '2005', 'type': 'B', 'city': 'Cairo', 'event': 'World Championships', 'category': '-90kg' },
+ { 'year': '2005', 'type': 'B', 'city': 'Rotterdam', 'event': 'European Championships', 'category': '-90kg' },
+ { 'year': '2008', 'type': 'G', 'city': 'Lisbon', 'event': 'European Championships', 'category': '-90kg' },
+ ],
+ },
+ {
+ 'id': 'rishod-sobirov',
+ 'name': 'Rishod Sobirov',
+ 'country': 'uz',
+ 'birth': '1986',
+ 'image': 'rishod-sobirov.jpg',
+ 'cover': 'rishod-sobirov-cover.jpg',
+ 'link': 'https://en.wikipedia.org/wiki/Rishod_Sobirov',
+ 'medals': [
+ { 'year': '2007', 'type': 'S', 'city': 'Kuwait City', 'event': 'Asian Championships', 'category': '-60kg' },
+ { 'year': '2008', 'type': 'B', 'city': 'Beijing', 'event': 'Olympic Games', 'category': '-60kg' },
+ { 'year': '2010', 'type': 'G', 'city': 'Tokyo', 'event': 'World Championships', 'category': '-60kg' },
+ { 'year': '2011', 'type': 'G', 'city': 'Paris', 'event': 'World Championships', 'category': '-60kg' },
+ { 'year': '2012', 'type': 'B', 'city': 'London', 'event': 'Olympic Games', 'category': '-60kg' },
+ { 'year': '2015', 'type': 'B', 'city': 'Astana', 'event': 'World Championships', 'category': '-66kg' },
+ { 'year': '2016', 'type': 'B', 'city': 'Rio de Janeiro', 'event': 'Olympic Games', 'category': '-66kg' },
+ ],
+ },
+ {
+ 'id': 'ryoko-tani',
+ 'name': 'Ryoko Tani',
+ 'country': 'jp',
+ 'birth': '1975',
+ 'image': 'ryoko-tani.jpg',
+ 'cover': 'ryoko-tani-cover.jpg',
+ 'link': 'https://en.wikipedia.org/wiki/Ryoko_Tani',
+ 'medals': [
+ { 'year': '1991', 'type': 'B', 'city': 'Barcelona', 'event': 'World Championships', 'category': '-48kg' },
+ { 'year': '1991', 'type': 'B', 'city': 'Osaka', 'event': 'Asian Championships', 'category': '-48kg' },
+ { 'year': '1992', 'type': 'S', 'city': 'Barcelona', 'event': 'Olympic Games', 'category': '-48kg' },
+ { 'year': '1993', 'type': 'G', 'city': 'Hamilton', 'event': 'World Championships', 'category': '-48kg' },
+ { 'year': '1994', 'type': 'G', 'city': 'Hiroshima', 'event': 'Asian Games', 'category': '-48kg' },
+ { 'year': '1995', 'type': 'G', 'city': 'Chiba', 'event': 'World Championships', 'category': '-48kg' },
+ { 'year': '1995', 'type': 'G', 'city': 'Fukuoka', 'event': 'Universiade', 'category': '-48kg' },
+ { 'year': '1996', 'type': 'S', 'city': 'Atlanta', 'event': 'Olympic Games', 'category': '-48kg' },
+ { 'year': '1997', 'type': 'G', 'city': 'Paris', 'event': 'World Championships', 'category': '-48kg' },
+ { 'year': '1999', 'type': 'G', 'city': 'Birmingham', 'event': 'World Championships', 'category': '-48kg' },
+ { 'year': '2000', 'type': 'G', 'city': 'Sydney', 'event': 'Olympic Games', 'category': '-48kg' },
+ { 'year': '2001', 'type': 'G', 'city': 'Munich', 'event': 'World Championships', 'category': '-48kg' },
+ { 'year': '2003', 'type': 'G', 'city': 'Osaka', 'event': 'World Championships', 'category': '-48kg' },
+ { 'year': '2004', 'type': 'G', 'city': 'Athens', 'event': 'Olympic Games', 'category': '-48kg' },
+ { 'year': '2007', 'type': 'G', 'city': 'Rio de Janeiro', 'event': 'World Championships', 'category': '-48kg' },
+ { 'year': '2008', 'type': 'B', 'city': 'Beijing', 'event': 'Olympic Games', 'category': '-48kg' },
+ ],
+ },
+ {
+ 'id': 'teddy-riner',
+ 'name': 'Teddy Riner',
+ 'country': 'fr',
+ 'birth': '1989',
+ 'image': 'teddy-riner.jpg',
+ 'cover': 'teddy-riner-cover.jpg',
+ 'link': 'https://en.wikipedia.org/wiki/Teddy_Riner',
+ 'medals': [
+ { 'year': '2007', 'type': 'G', 'city': 'Belgrade', 'event': 'European Championships', 'category': '+100kg' },
+ { 'year': '2007', 'type': 'G', 'city': 'Rio de Janeiro', 'event': 'World Championships', 'category': '+100kg' },
+ { 'year': '2008', 'type': 'B', 'city': 'Beijing', 'event': 'Olympic Games', 'category': '+100kg' },
+ { 'year': '2008', 'type': 'G', 'city': 'Levallois-Perret', 'event': 'World Openweight Championships', 'category': 'Open' },
+ { 'year': '2009', 'type': 'G', 'city': 'Pescara', 'event': 'Mediterranean Games', 'category': '+100kg' },
+ { 'year': '2009', 'type': 'G', 'city': 'Rotterdam', 'event': 'World Championships', 'category': '+100kg' },
+ { 'year': '2010', 'type': 'G', 'city': 'Tokyo', 'event': 'World Championships', 'category': '+100kg' },
+ { 'year': '2010', 'type': 'S', 'city': 'Tokyo', 'event': 'World Championships', 'category': 'Open' },
+ { 'year': '2011', 'type': 'G', 'city': 'Istanbul', 'event': 'European Championships', 'category': '+100kg' },
+ { 'year': '2011', 'type': 'G', 'city': 'Paris', 'event': 'World Championships', 'category': '+100kg' },
+ { 'year': '2012', 'type': 'G', 'city': 'London', 'event': 'Olympic Games', 'category': '+100kg' },
+ { 'year': '2013', 'type': 'G', 'city': 'Budapest', 'event': 'European Championships', 'category': '+100kg' },
+ { 'year': '2013', 'type': 'G', 'city': 'Rio de Janeiro', 'event': 'World Championships', 'category': '+100kg' },
+ { 'year': '2014', 'type': 'G', 'city': 'Chelyabinsk', 'event': 'World Championships', 'category': '+100kg' },
+ { 'year': '2014', 'type': 'G', 'city': 'Montpellier', 'event': 'European Championships', 'category': '+100kg' },
+ { 'year': '2015', 'type': 'G', 'city': 'Astana', 'event': 'World Championships', 'category': '+100kg' },
+ { 'year': '2016', 'type': 'G', 'city': 'Kazan', 'event': 'European Championships', 'category': '+100kg' },
+ { 'year': '2016', 'type': 'G', 'city': 'Rio de Janeiro', 'event': 'Olympic Games', 'category': '+100kg' },
+ ],
+ }
+];
+
+export default athletes;
diff --git a/frontendJS/universal-react/src/routes.js b/frontendJS/universal-react/src/routes.js
new file mode 100644
index 0000000..d5854db
--- /dev/null
+++ b/frontendJS/universal-react/src/routes.js
@@ -0,0 +1,16 @@
+import React from 'react';
+import { Route, IndexRoute } from 'react-router';
+import Layout from './components/Layout';
+import IndexPage from './components/IndexPage';
+import AthletePage from './components/AthletePage';
+import NotFoundPage from './components/NotFoundPage';
+
+const routes = (
+
+
+
+
+
+);
+
+export default routes;
\ No newline at end of file
diff --git a/frontendJS/universal-react/src/server.js b/frontendJS/universal-react/src/server.js
new file mode 100644
index 0000000..c9f4eaf
--- /dev/null
+++ b/frontendJS/universal-react/src/server.js
@@ -0,0 +1,56 @@
+import path from 'path';
+import { Server } from 'http';
+import Express from 'express';
+import React from 'react';
+import { renderToString } from 'react-dom/server';
+import { match, RouterContext } from 'react-router';
+import routes from './routes';
+import NotFoundPage from './components/NotFoundPage';
+
+// initialize the server and configure support for ejs templates
+const app = new Express();
+const server = new Server(app);
+app.set('view engine', 'ejs');
+app.set('views', path.join(__dirname, '..', 'views'));
+
+// define the folder that will be used for public assets
+app.use(Express.static(path.join(__dirname, '..', 'public')));
+
+// universal routing and rendering
+app.get('*', (req, res) => {
+ match({routes, location: req.url}, (err, redirectLocation, renderProps) => {
+ // Directly send errors
+ if (err) {
+ return res.status(500).send(err.message);
+ }
+
+ // Propagate redirects to the browser
+ if (redirectLocation) {
+ return res.redirect(302, redirectLocation.pathname + redirectLocation.search);
+ }
+
+ // generate the React markup for the current route
+ let markup;
+ if (renderProps) {
+ // if the current route matched, we have renderProps
+ markup = renderToString();
+ } else {
+ markup = renderToString();
+ res.status(404);
+ }
+
+ // render the index template with the embedded React markup
+ return res.render('index', { markup:markup });
+ });
+});
+
+// start the server
+const port = process.env.PORT || 3000;
+const env = process.env.NODE_ENV || 'production';
+
+server.listen(port, err => {
+ if (err) {
+ return console.error(err);
+ }
+ console.info(`Server running on http://localhost:${port} [${env}]`);
+});
\ No newline at end of file
diff --git a/frontendJS/universal-react/views/index.ejs b/frontendJS/universal-react/views/index.ejs
new file mode 100644
index 0000000..9129e98
--- /dev/null
+++ b/frontendJS/universal-react/views/index.ejs
@@ -0,0 +1,14 @@
+
+
+
+
+
+ Judo Heroes - A Universal Javascript demo application with React
+
+
+
+
+ <%- markup -%>
+
+
+
\ No newline at end of file
diff --git a/frontendJS/universal-react/webpack.config.js b/frontendJS/universal-react/webpack.config.js
new file mode 100644
index 0000000..406a076
--- /dev/null
+++ b/frontendJS/universal-react/webpack.config.js
@@ -0,0 +1,36 @@
+const webpack = require('webpack');
+const path = require('path');
+
+module.exports = {
+ entry: path.join(__dirname, 'src', 'app-client.js'),
+ output: {
+ path: path.join(__dirname, 'public', 'js'),
+ filename: 'bundle.js'
+ },
+ module: {
+ loaders: [{
+ test: path.join(__dirname, 'src'),
+ loader: ['babel-loader'],
+ query: {
+ cacheDirectory: 'babel_cache',
+ presets: ['react', 'es2015']
+ }
+ }]
+ },
+ plugins: [
+ new webpack.DefinePlugin({
+ 'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV)
+ }),
+ new webpack.optimize.DedupePlugin(),
+ new webpack.optimize.OccurrenceOrderPlugin(),
+ new webpack.optimize.UglifyJsPlugin({
+ compress: {
+ warnings: false
+ },
+ mangle: true,
+ sourcemap: false,
+ beautify: false,
+ dead_code: true
+ }),
+ ]
+};
\ No newline at end of file