From 9e0cb87efef3fefdff7d7cd23d72826d1aa5aac5 Mon Sep 17 00:00:00 2001
From: andreasonny83 <andreasonny83@gmail.com>
Date: Mon, 17 Apr 2017 13:35:44 +0100
Subject: [PATCH] Major refactoring

---
 gulpfile.js                          | 14 ------------
 package.json                         |  8 ++++---
 src/app/config.js                    | 14 +++++++-----
 src/app/game/game.controller.js      | 11 +++++++--
 src/app/game/game.html               | 22 ++++++++++--------
 src/app/page-footer/page-footer.html |  1 -
 src/css/main.css                     | 34 ++++++++++++++++++++++++----
 src/index.html                       | 18 ++++++++-------
 src/index.js                         | 19 ++++++++++++++++
 src/sass/modules/_page-footer.scss   |  6 ++++-
 src/sass/modules/_page-header.scss   |  6 +++--
 src/sass/partials/_helpers.scss      | 20 +++++++++++++++-
 src/sw.js                            | 10 +++-----
 13 files changed, 125 insertions(+), 58 deletions(-)

diff --git a/gulpfile.js b/gulpfile.js
index 3ecca46..68f9a54 100644
--- a/gulpfile.js
+++ b/gulpfile.js
@@ -101,19 +101,6 @@ gulp.task('copy', function() {
     .pipe(gulp.dest('./dist/'));
 });
 
-gulp.task('copy:prod', function() {
-  // rename and uglify config.prod.js if present
-  // otherwise use config.js
-  return gulp
-    .src([
-      './src/app/config.js',
-      './src/app/config.prod.js'
-    ])
-    .pipe($.rename('config.js'))
-    .pipe($.uglify())
-    .pipe(gulp.dest('./dist/js/'));
-});
-
 gulp.task('move:dist', function() {
   // rename and uglify config.prod.js if present
   // otherwise use config.js
@@ -251,7 +238,6 @@ gulp.task('build', function(callback) {
     'templates',
     'usemin',
     'version',
-    'copy:prod',
     callback);
 });
 
diff --git a/package.json b/package.json
index e125689..fb19db0 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "carcassonne-scoreboard-client",
-  "version": "1.2.0",
+  "version": "1.3.0",
   "author": "Andrea Sonny <andreasonny83@gmail.com>",
   "description": "Online Scoreboard for the famous boardgame Carcassonne",
   "main": "index.js",
@@ -18,11 +18,13 @@
   "scripts": {
     "bower": "bower",
     "postinstall": "bower install",
-    "test": "./node_modules/.bin/karma start karma.conf.js --single-run --browsers PhantomJS",
+    "karma": "karma",
+    "test": "karma start karma.conf.js --single-run --browsers PhantomJS",
     "start": "gulp serve",
     "build": "gulp build",
+    "nodemon": "nodemon",
     "preserve:dist": "gulp build",
-    "serve:dist": "gulp serve:dist",
+    "serve:dist": "nodemon dist/index.js",
     "heroku-postbuild": "gulp deploy"
   },
   "contributors": [],
diff --git a/src/app/config.js b/src/app/config.js
index e71793a..cf612b2 100644
--- a/src/app/config.js
+++ b/src/app/config.js
@@ -10,12 +10,14 @@
 (function() {
   'use strict';
 
+  var env = {};
+
+  // Import variables if present (from env.js)
+  if (window) {
+    Object.assign(env, window.__env);
+  }
+
   angular
     .module('app')
-    .constant('serverApp', {
-      server : 'https://carcassonne-scoreboard.herokuapp.com', // server
-      port   : 443
-      // server : 'http://localhost',
-      // port   : 5000
-    });
+    .constant('serverApp', env);
 })();
diff --git a/src/app/game/game.controller.js b/src/app/game/game.controller.js
index a6bcf14..01a3adb 100644
--- a/src/app/game/game.controller.js
+++ b/src/app/game/game.controller.js
@@ -14,10 +14,10 @@
         .module('app')
         .controller('GameController', GameController);
 
-    GameController.$inject = ['$scope', '$location', '$routeParams', '$mdToast', '$mdDialog', 'gameType', 'socket'];
+    GameController.$inject = ['$anchorScroll', '$scope', '$location', '$routeParams', '$mdToast', '$mdDialog', 'gameType', 'socket'];
 
     /* @ngInject */
-    function GameController($scope, $location, $routeParams, $mdToast, $mdDialog, gameType, socket) {
+    function GameController($anchorScroll, $scope, $location, $routeParams, $mdToast, $mdDialog, gameType, socket) {
       var vm = this,
           new_game = {
             name: 'New game',
@@ -68,6 +68,13 @@
           color: assignRndMeeple(),
           score: 0
         });
+
+        var body = document.querySelector('div.page');
+
+        setTimeout(function() {
+          body.scrollTop = body.scrollHeight;
+          document.querySelector('#player_' + vm.game.players.length + ' input').focus();
+        }, 0);
       }
 
       vm.removePlayer = function(key) {
diff --git a/src/app/game/game.html b/src/app/game/game.html
index 9b32dd4..82f25f0 100644
--- a/src/app/game/game.html
+++ b/src/app/game/game.html
@@ -1,8 +1,8 @@
 <div class="page-header-sub">
   <md-content>
     <section layout="row" layout-align="start start" layout-align-sm="center center" layout-wrap>
-      <md-button flex-xs="100" flex-sm="auto" flex-md="nogrow" class="md-raised cs-button-back" ng-click="mainCtrl.goTo('/')" tabindex="17">Home</md-button>
-      <md-button flex-xs="100" flex-sm="auto" flex-md="nogrow" class="md-raised cs-button-back" ng-hide="{{gameCtrl.new_game}}" ng-click="gameCtrl.startGame()" tabindex="16">Resume</md-button>
+      <md-button flex-xs="100" flex-sm="auto" flex-md="nogrow" class="md-raised cs-button-back" ng-click="mainCtrl.goTo('/')">Home</md-button>
+      <md-button flex-xs="100" flex-sm="auto" flex-md="nogrow" class="md-raised cs-button-back" ng-hide="{{gameCtrl.new_game}}" ng-click="gameCtrl.startGame()">Resume</md-button>
     </section>
   </md-content>
   <h2 class="home">{{gameCtrl.game.name}}</h2>
@@ -22,7 +22,7 @@ <h2 class="section-title">Game name</h2>
       <div layout="row" layout-align="center">
         <md-input-container flex="50">
           <label>Game name</label>
-          <input ng-model="gameCtrl.game.name" tabindex="1">
+          <input ng-model="gameCtrl.game.name" md-autofocus>
         </md-input-container>
       </div>
 
@@ -32,16 +32,20 @@ <h2 class="section-title">Players</h2>
         <md-divider></md-divider>
 
         <!-- User list -->
-        <md-list-item class="md-3-line md-no-proxy secondary-button-padding md-with-secondary" ng-repeat="(key, player) in gameCtrl.game.players">
+        <md-list-item class="md-3-line md-no-proxy secondary-button-padding md-with-secondary"
+                      ng-repeat="(key, player) in gameCtrl.game.players"
+                      id="player_{{key+1}}">
           <div class="md-list-item-text">
             <md-input-container class="input-container">
               <label>Player name</label>
-              <input ng-model="player.name" tabindex="{{key + (key + 3)}}">
+              <input ng-model="player.name"
+                     ng-focus="$event.target.select()"
+                     md-autofocus>
             </md-input-container>
 
             <md-input-container class="input-container">
               <label>Player color</label>
-              <md-select ng-model="player.color" tabindex="{{key + (key + 4)}}">
+              <md-select ng-model="player.color">
                 <md-option ng-repeat="meeple in gameCtrl.game.meeples" value="{{meeple}}">
                   {{meeple}}
                 </md-option>
@@ -62,14 +66,14 @@ <h2 class="section-title">Players</h2>
       </md-list>
       <!-- User list End -->
 
-      <md-button class="md-raised cs-button-primary cs-button-md" ng-click="gameCtrl.addPlayer()" tabindex="2">Add player</md-button>
+      <md-button class="md-raised cs-button-primary cs-button-md" ng-click="gameCtrl.addPlayer()">Add player</md-button>
 
     </md-card-content>
 
     <md-card-actions layout="row" layout-align="end center">
       <md-button class="cs-alert" ng-click="gameCtrl.deleteGame()">Delete game</md-button>
-      <md-button ng-hide="{{gameCtrl.new_game}}" ng-click="gameCtrl.startGame()" tabindex="16">Resume Game</md-button>
-      <md-button ng-show="{{gameCtrl.new_game}}" ng-click="gameCtrl.startGame()" tabindex="16">Start Game</md-button>
+      <md-button ng-hide="{{gameCtrl.new_game}}" ng-click="gameCtrl.startGame()">Resume Game</md-button>
+      <md-button ng-show="{{gameCtrl.new_game}}" ng-click="gameCtrl.startGame()">Start Game</md-button>
     </md-card-actions>
   </md-card>
 </section>
diff --git a/src/app/page-footer/page-footer.html b/src/app/page-footer/page-footer.html
index a2e2405..95efda1 100644
--- a/src/app/page-footer/page-footer.html
+++ b/src/app/page-footer/page-footer.html
@@ -3,7 +3,6 @@
     <div class="row">
       <p>Carcassonne Online Scoreboard %%GULP_INJECT_VERSION%%</p>
       <p>Proudly developed by <a href="https://github.com/andreasonny83" alt="andreasonny83 on GitHub">&lt;SonnY&gt;</a>. All Rights Reserved</p>
-      <p>This site uses cookies. By continuing to browse the site, you are agreeing to our use of cookies. <a href="#/privacy-policy" alt="andreasonny83 on GitHub">Find out more</a>.</p>
     </div>
   </div>
 </footer>
diff --git a/src/css/main.css b/src/css/main.css
index 4e96de6..f339f15 100644
--- a/src/css/main.css
+++ b/src/css/main.css
@@ -429,8 +429,28 @@ Animations
   }
 }
 
+body {
+  overflow: hidden;
+}
+
+#app {
+  height: 100%;
+}
+
 .page.ng-scope {
-  margin-bottom: 140px;
+  overflow-y: auto;
+  position: absolute;
+  left: 0;
+  top: 65px;
+  bottom: 59px;
+  width: 100%;
+}
+
+@media all and (min-height: 600px) {
+  .page.ng-scope {
+    top: 114px;
+    bottom: 84px;
+  }
 }
 
 section {
@@ -753,13 +773,13 @@ It means that loaders will fallback to a text state of simply saying "Loading...
   font-size: 32px;
 }
 
-@media screen and (max-width: 600px) {
+@media all and (max-width: 600px), all and (max-height: 600px) {
   .header h1 {
     font-size: 26px;
   }
 }
 
-@media screen and (max-width: 600px) {
+@media all and (max-width: 600px), all and (max-height: 600px) {
   .header .desc {
     display: none;
   }
@@ -781,7 +801,7 @@ It means that loaders will fallback to a text state of simply saying "Loading...
 }
 
 .footer {
-  padding: 30px 0 15px;
+  padding: 10px 0;
   position: fixed;
   bottom: 0;
   left: 0;
@@ -791,6 +811,12 @@ It means that loaders will fallback to a text state of simply saying "Loading...
   text-align: center;
 }
 
+@media all and (min-height: 600px) {
+  .footer {
+    padding: 30px 0 15px;
+  }
+}
+
 .footer p {
   padding: 0 10px 5px 10px;
   margin: 0;
diff --git a/src/index.html b/src/index.html
index 1d2a6a4..c241960 100644
--- a/src/index.html
+++ b/src/index.html
@@ -78,6 +78,9 @@
   <!-- endbuild -->
   <script>document.documentElement.className = document.documentElement.className.replace("no-js","js");</script>
 
+  <!-- Load environment variables -->
+  <script src="env.js"></script>
+
   <!-- Google Analytics -->
   <script>
     (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
@@ -101,12 +104,14 @@
     </div>
   </div>
 
-  <page-header></page-header>
+  <div id="app">
+    <page-header></page-header>
 
-  <!-- Main view for templates -->
-  <div ng-view class="page" autoscroll="true"></div>
+    <!-- Main view for templates -->
+    <div ng-view class="page" autoscroll="true"></div>
 
-  <page-footer></page-footer>
+    <page-footer></page-footer>
+  </div>
 
   <!-- Non-angular libraries -->
   <!-- build:libs js/libs.js -->
@@ -125,14 +130,11 @@
 
   <!-- build:appcomponents js/appcomponents.js -->
   <script src="app/app.js"></script>
+  <script src="app/config.js"></script>
   <script src="app/page-header/page-header.directive.js"></script>
   <script src="app/page-footer/page-footer.directive.js"></script>
   <!-- endbuild -->
 
-  <!-- build:angularconfig js/config.js -->
-  <script src="app/config.js"></script>
-  <!-- endbuild -->
-
   <!-- build:mainapp js/mainapp.js -->
   <!-- Services -->
   <script src="app/services/socket.js"></script>
diff --git a/src/index.js b/src/index.js
index 26ef740..ec74e3c 100644
--- a/src/index.js
+++ b/src/index.js
@@ -9,6 +9,25 @@ app.get('/', function(req, res) {
   res.sendFile('index.html');
 });
 
+// dynamic config vars are fetched from global environment variables, if any
+app.get('/env.js', function(req, res) {
+    res.send(`
+      (function (window) {
+        window.__env = window.__env || {};
+
+        // API url
+        window.__env.server = "${process.env.API_URL || 'http://localhost'}";
+
+        // API port number
+        window.__env.port = ${process.env.API_PORT || 5000};
+
+        // Whether or not to enable debug mode
+        // Setting this to false will disable console output
+        window.__env.enableDebug = ${process.env.DEBUG || false};
+      }(this));
+    `);
+});
+
 app.listen(port, function() {
   console.log('Client app listening on port ' + port);
 });
diff --git a/src/sass/modules/_page-footer.scss b/src/sass/modules/_page-footer.scss
index 46b3825..5cf9b77 100644
--- a/src/sass/modules/_page-footer.scss
+++ b/src/sass/modules/_page-footer.scss
@@ -1,5 +1,5 @@
 .footer {
-  padding: 30px 0 15px;
+  padding: 10px 0;
   position: fixed;
   bottom: 0;
   left: 0;
@@ -8,6 +8,10 @@
   background-color: rgb(251, 245, 223);
   text-align: center;
 
+  @media all and (min-height: $break-large) {
+    padding: 30px 0 15px;
+  }
+
   p {
     padding: 0 10px 5px 10px;
     margin: 0;
diff --git a/src/sass/modules/_page-header.scss b/src/sass/modules/_page-header.scss
index 2f699d3..a21376a 100644
--- a/src/sass/modules/_page-header.scss
+++ b/src/sass/modules/_page-header.scss
@@ -8,13 +8,15 @@
   h1 {
     font-size: 32px;
 
-    @media screen and (max-width: $break-large) {
+    @media all and (max-width: $break-large),
+           all and (max-height: $break-large) {
       font-size: 26px;
     }
   }
 
   .desc {
-    @media screen and (max-width: $break-large) {
+    @media all and (max-width: $break-large),
+           all and (max-height: $break-large) {
       display: none;
     }
   }
diff --git a/src/sass/partials/_helpers.scss b/src/sass/partials/_helpers.scss
index 5c86f8b..96647ac 100644
--- a/src/sass/partials/_helpers.scss
+++ b/src/sass/partials/_helpers.scss
@@ -1,5 +1,23 @@
+body {
+  overflow: hidden;
+}
+
+#app {
+  height: 100%;
+}
+
 .page.ng-scope {
-  margin-bottom: 140px;
+  overflow-y: auto;
+  position: absolute;
+  left: 0;
+  top: 65px;
+  bottom: 59px;
+  width: 100%;
+
+  @media all and (min-height: $break-large) {
+    top: 114px;
+    bottom: 84px;
+  }
 }
 
 section {
diff --git a/src/sw.js b/src/sw.js
index 5d4a230..38e3441 100644
--- a/src/sw.js
+++ b/src/sw.js
@@ -1,4 +1,4 @@
-var CACHE_NAME = 'carcassonne-scoreboard-client::v6';
+var CACHE_NAME = 'carcassonne-scoreboard-client::v7';
 // The files we want to cache
 var urlsToCache = [
   '/',
@@ -7,14 +7,10 @@ var urlsToCache = [
   'js/angularlibs.js',
   'js/libs.js',
   'js/appcomponents.js',
-  'js/config.js',
   'js/mainapp.js',
   'js/templates.js',
-  'fonts/carcassonne-scoreboard-font/carcassonne-scoreboard-font.eot',
-  'fonts/carcassonne-scoreboard-font/carcassonne-scoreboard-font.ttf',
-  'fonts/carcassonne-scoreboard-font/carcassonne-scoreboard-font.woff',
-  'fonts/carcassonne-scoreboard-font/carcassonne-scoreboard-font.woff2',
-  'fonts/carcassonne-scoreboard-font/carcassonne-scoreboard-font.svg'
+  'images/*.*',
+  'fonts/carcassonne-scoreboard-font/*.*'
 ];
 
 // Set the callback for the install step