<commit_before>let app = angular.module('timecrunch', ['ui.router']);

app.config([
'$stateProvider',
'$urlRouterProvider',
function($stateProvider, $urlRouterProvider) {
  $stateProvider
    .state('home', {
      url: '/home',
      templateUrl: '/templates/home.html',
      controller: 'DashCtrl'
    })
    .state('food', {
      url: '/food',
      templateUrl: '/templates/food.html',
      controller: 'ListCtrl',
      resolve: {
        foodPromise: ['foods', function(foods) {
            return foods.getFood();
        }]
      }
    })
    .state('recipes', {
      url: '/recipes',
      templateUrl: '/templates/recipes.html',
      controller: 'ListCtrl',
      resolve: {
        recipePromise: ['recipes', function(recipes) {
            return recipes.getAll();
        }]
      }
    })
    .state('recipeform', {
      url: '/recipes/form',
      templateUrl: '/templates/recipeform.html',
      controller: 'ListCtrl'
    })
    .state('recipe', {
      url: '/recipes/{id}',
      templateUrl: '/templates/recipe.html',
      controller: 'RecipesCtrl',
      resolve: {
        recipe: ['$stateParams', 'recipes', function($stateParams, recipes) {
          return recipes.get($stateParams.id);
        }]
      }
    })
    .state('editRecipe', {
      url: '/recipes/{id}/edit',
      templateUrl: '/templates/recipeeditform.html',
      controller: 'RecipesCtrl',
      resolve: {
        recipe: ['$stateParams', 'recipes', function($stateParams, recipes) {
          return recipes.get($stateParams.id);
        }]
      }
    })
    .state('login', {
      url: '/login',
      templateUrl: '/templates/login.html',
      controller: 'AuthCtrl',
      onEnter: ['$state', 'auth', function($state, auth) {
        if(auth.isLoggedIn()) {
          $state.go('home');
        }
      }]
    })
    .state('register', {
      url: '/register',
      templateUrl: '/templates/register.html',
      controller: 'AuthCtrl',
      onEnter: ['$state', 'auth', function($state, auth) {
        if(auth.isLoggedIn()) {
          $state.go('home');
        }
      }]
    });

  $urlRouterProvider.otherwise('home');
}]);

// Factory for auth handling all authorization and
// user id related functions
app.factory('auth', ['$http', '$window', function($http, $window) {
  let auth = {};

  // save given token
  auth.saveToken = function(token) {
    $window.localStorage['time-crunch-token'] = token;
  };

  // get token from localStorage
  auth.getToken = function() {
    return $window.localStorage['time-crunch-token'];
  };

  // register user
  auth.register = function(user) {
    return $http.post('/register', user).then(function(data) {
      auth.saveToken(data.data.token);
    });
  };

  // login user save token
  auth.logIn = function(user) {
    return $http.post('/login', user).then(function(data) {
      auth.saveToken(data.data.token);
    });
  };

  // logout a user and delete token
  auth.logOut = function() {
    $window.localStorage.removeItem('time-crunch-token');
  };

  // check if user is logged in
  auth.isLoggedIn = function() {
    let token = auth.getToken();

    if(token) {
      let payload = JSON.parse($window.atob(token.split('.')[1]));

      return payload.exp > Date.now() / 1000;
    } else {
      return false;
    }
  };

  // returns currently logged in username
  auth.currentUser = function() {
    if(auth.isLoggedIn()) {
      let token = auth.getToken();
      let payload = JSON.parse($window.atob(token.split('.')[1]));

      return payload.username;
      }
    };

  return auth;
}]);

// Factory for foods handling all related functions
app.factory('foods', ['$http', '$state', 'auth', function($http, $state, auth) {
  let o = {
    foods: []
  };

  // Load food list for the current user
  o.getFood = function() {
    if(auth.isLoggedIn()) {
      return $http.get('/food', {
        headers: {Authorization: 'Bearer ' + auth.getToken()}
      }).then(function(data) {
        angular.copy(data.data, o.foods);
      });
    }
    $state.go('login');
  };

  // Add a food to a user's food list
  o.create = function(food) {
    return $http.post('/food', food, {
      headers: {Authorization: 'Bearer ' + auth.getToken()}
    }).then(function(data) {
      o.getFood();
    });
  };

  // Update a specific food
  o.update = function(food) {
    return $http.put('/food/' + food._id, food, {
      headers: {Authorization: 'Bearer ' + auth.getToken()}
    }).then(function(data) {
      o.getFood();
    });
  }

  // Remove a food from a user's food list
  o.delete = function(food) {
    return $http.delete('/food/' + food._id).then(function(data) {
      o.getFood();
    });
  };

  return o;
}]);

// Factory for recipes handling related functions
app.factory('recipes', ['$http', 'auth', function($http, auth) {
  let o = {
    recipes: []
  };

  // Get all recipes
  o.getAll = function() {
    if(auth.isLoggedIn()) {
      return $http.get('/recipes', {
        headers: {Authorization: 'Bearer ' + auth.getToken()}
      }).then(function(data) {
        angular.copy(data.data, o.recipes);
      });
    }
    return $http.get('/recipes/public').then(function(data) {
      angular.copy(data.data, o.recipes);
    });
  };

  // Get a specific recipe
  o.get = function(id) {
    return $http.get('/recipes/' + id).then(function(data) {
      return data.data;
    });
  };

  // Create a new recipe
  o.create = function(recipe) {
    return $http.post('/recipes', recipe, {
      headers: {Authorization: 'Bearer ' + auth.getToken()}
    }).then(function(data) {
      return data.data._id;
    });
  };

  // Update a recipe
  o.update = function(recipe) {
    return $http.put('/recipes/' + recipe._id, recipe, {
      headers: {Authorization: 'Bearer ' + auth.getToken()}
    }).then(function(data) {
      o.getAll();
    });
  };

  // Upvote a recipe
  o.upvote = function(recipe) {
    return $http.put('/recipes/' + recipe._id + '/upvote', null, {
      headers: {Authorization: 'Bearer ' + auth.getToken()}
    }).then(function(data) {
      recipe.score += 1;
    });
  };

  // Delete a recipe
  o.delete = function(recipe) {
    return $http.delete('/recipes/' + recipe._id).then(function(data) {
      o.getAll();
    });
  };

  return o;
}]);

// Factory for ingredients handling related functions
app.factory('ingredients', ['$http', function($http){
  let o = {};

  // Create a new ingredient
  o.create = function(recipeID, ingredient) {
    return $http.post('/recipes/' + recipeID + '/ingredients',
      ingredient).then(function(data) {
        return data.data;
    });
  };

  // Delete an ingredient
  o.delete = function(recipeID, ingredient) {
    return $http.delete('/recipes/' + recipeID + 'ingredients/' + ingredient._id);
  };

  return o;
}]);

// Controller for dashboard on home/index
app.controller('DashCtrl', [
  '$scope',
  'foods',
  function($scope, foods){
    $scope.foods = foods.foods;
}]);

// Controller for recipe and food lists
app.controller('ListCtrl', [
  '$scope',
  '$state',
  'auth',
  'ingredients',
  'foods',
  'recipes',
  function($scope, $state, auth, ingredients, foods, recipes) {

    $scope.foods = foods.foods;
    $scope.recipes = recipes.recipes;
    $scope.ingredients = [];
    $scope.isLoggedIn = auth.isLoggedIn;

    $scope.categories = [
      "Baking",
      "Dairy",
      "Fruit",
      "Herb/Spice",
      "Meat",
      "Sauce",
      "Vegetable",
      "Other-Ingredient",
      "Other-Food",
      "Other-Produce"
    ];

    $scope.sortBy = ["score", "sellBy", "name"];
    $scope.filterCat = "";

    // Set containing ingredient names
    let ingNames = new Set();

    // Function used to filter food by category
    $scope.filter = function() {
      return function(item) {
        if($scope.filterCat == "" || !$scope.filterCat
          || $scope.filterCat == item.category) {
          return true;
        }
        return false;
      };
    };

    // Add a new food
    $scope.addFood = function() {
      // Make sure food name is valid
      if(!$scope.name || $scope.name == '') {
        alert("Please fill in the name field");
        return;
      }
      // If sell by was left blank, set it to 2 years from now
      if(!$scope.sellBy) {
        $scope.sellBy = new Date();
        $scope.sellBy.setDate($scope.sellBy.getDate() + 730);
      }
      foods.create({
        name: $scope.name,
        sellBy: $scope.sellBy,
        amount: $scope.amount,
        units: $scope.units,
        category: $scope.category
      });
    };

    // Update specific food
    $scope.updateFood = function(food) {
      // Make sure food name is valid
      if(!food.name || food.name == '') {
        alert("Please fill in the name field");
        return;
      }

      foods.update(food).then(function success() {
        // Reload food list
        $state.go('food');
      }, function failure(error) {
        // Show error message
        $scope.error = error.data;
      });
    }

    // Remove a food
    $scope.removeFood = function(food) {
      foods.delete(food);
    };

    // Add a new recipe
    $scope.addRecipe = function() {
      //Check that the recipe has a name, ingredients, and instructions
      if(!$scope.name || $scope.name == '' || !$scope.ingredients) {
        $scope.error = {
          message: "Please make sure all fields are filled out"
        };
        return;
      }

      if(!$scope.instructions && !$scope.link) {
        $scope.error= {
          message: "Please provide either instructions or a link to the full recipe"
        };
        return;
      }

      recipes.create({
        name: $scope.name,
        prepTime: $scope.prepTime,
        link: $scope.link,
        instructions: $scope.instructions,
        private: $scope.private
      }).then(function success(id) {
        // Return to the recipe list
        for(i = 0; i < $scope.ingredients.length; i++) {
          ingredients.create(id, $scope.ingredients[i]);
        }
        $scope.recipes = recipes.recipes;
        $state.go('recipes');
      }, function failure(error) {
        // Show error message
        $scope.error = error.data;
      });
    };

    // Upvote specific recipe
    $scope.upvoteRecipe = function(recipe) {
      recipes.upvote(recipe);
    };

    // Add an ingredient to a recipe
    $scope.addIngredient = function() {
      // Load existing ingredients into ingNames
      if(ingNames.size == 0 && $scope.ingredients.length > 0) {
        for(i = 0; i < $scope.ingredients.length; i++) {
          ingNames.add($scope.ing_name);
        }
      }
      // If ingredient already exists show alert
      if(ingNames.has($scope.ing_name)) {
        $scope.error = {
          message: "Duplicate ingredient, please change ingredient name"
        };
        return;
      }

      if($scope.ing_name) {
        ($scope.ingredients).push({
          name: $scope.ing_name,
          amount: $scope.ing_amount,
          unit: $scope.ing_unit
        });
      }
      else {
        $scope.error = {
          message: "Missing ingredient name"
        };
      }

      // Add new ingredient name to the set
      ingNames.add($scope.ing_name);
    };

    // Remove an ingredient from a recipe
    $scope.removeIngredient = function(ingredient) {
      if($scope.ingredients.length == 1) {
        $scope.ingredients = [];
      }
      else {
        let i = $scope.ingredients.indexOf(ingredient);
        // Remove ingredient name from set
        ingNames.delete(ingredient.name);
        $scope.ingredients.splice(i, 1);
      }
    };
}]);

// Controller for recipe details page and edit form
app.controller('RecipesCtrl', [
  '$scope',
  '$state',
  'ingredients',
  'recipe',
  'recipes',
  'auth',
  function($scope, $state, ingredients, recipe, recipes, auth){
    $scope.recipe = recipe;
    // Set containing ingredient names
    let ingNames = new Set();

    // Determines if a user is allowed to edit this
    // recipe
    $scope.canEdit = function() {
      return recipe.author == auth.currentUser();
    };

    // Remove an ingredient from a recipe
    $scope.removeIngredient = function(ingredient) {
      if($scope.recipe.ingredients.length == 1) {
        $scope.recipe.ingredients = [];
        ingNames.clear();
      }
      else {
        let i = $scope.recipe.ingredients.indexOf(ingredient);
        // Remove ingredient name from set
        ingNames.delete(ingredient.name);
        $scope.recipe.ingredients.splice(i, 1);
      }
    };

    // Add an ingredient to a recipe
    $scope.addIngredient = function() {
      // Load existing ingredients into ingNames
      if(ingNames.size < $scope.recipe.ingredients.length) {
        for(i = 0; i < $scope.recipe.ingredients.length; i++) {
          ingNames.add($scope.ing_name);
        }
      }
      // If ingredient already exists show alert
      if(ingNames.has($scope.ing_name)) {
        $scope.error = {
          message: "Duplicate ingredient, please change ingredient name"
        };
        return;
      }
      // Build the ingredient string
      let ing = {
        name: $scope.ing_name,
        amount: $scope.ing_amount,
        unit: $scope.ing_unit
      };
      if(ing.name) {
        ($scope.recipe.ingredients).push(ing);
        // Add new ingredient name to the set
        ingNames.add(ing.name);
      }
      else {
        $scope.error = {
          message: "Missing ingredient name"
        };
      }
    };

    // Edit an existing recipe
    $scope.editRecipe = function() {
      for(i = 0; i < $scope.recipe.ingredients.length; i++) {
        if(!$scope.recipe.ingredients[i]._id) {
          ingredients.create($scope.recipe._id, $scope.recipe.ingredients[i]);
        }
      }
      recipes.update($scope.recipe).then(function success() {
        // Return to the recipe list
        $state.go('recipe', {id: recipe._id});
      }, function failure(error) {
        // Show error message
        $scope.error = error.data;
      });
    };

    // Upvote this recipe
    $scope.upvoteRecipe = function() {
      recipes.upvote($scope.recipe);
      $scope.recipe.score += 1;
      $scope.voted = true;
    };

    // Deletes the recipe
    $scope.deleteRecipe = function() {
      // Make sure user is authorized
      if($scope.canEdit()) {
        recipes.delete(recipe);
        $state.go('recipes');
      }
      else {
        // If not allowed show an alert
        alert('You cannot delete this recipe!');
      }
    };

}]);

// Controller for logging in and registering
app.controller('AuthCtrl', [
  '$scope',
  '$state',
  'auth',
  function($scope, $state, auth) {
    $scope.user = {};

    // Registers a new user
    $scope.register = function() {
      auth.register($scope.user).then(function success() {
        // Go to dashboard once logged in
        $state.go('home');
      }, function failure(error) {
        // Show error message
        $scope.error = error.data;
      });
    };

    // Starts a new login session
    $scope.logIn = function() {
      auth.logIn($scope.user).then(function success() {
        // Go to dashboard once logged in
        $state.go('home');
      }, function failure(error) {
        // Show error message
        $scope.error = error.data;
      });
    };
}]);

// Controller for dashboard on home/index
app.controller('NavCtrl', [
  '$scope',
  'auth',
  function($scope, auth){

    $scope.isLoggedIn = auth.isLoggedIn;
    $scope.currentUser = auth.currentUser;
    $scope.logOut = auth.logOut;
}]);<commit_msg>Recipe orderBy now uses score desc<commit_after>let app = angular.module('timecrunch', ['ui.router']);

app.config([
'$stateProvider',
'$urlRouterProvider',
function($stateProvider, $urlRouterProvider) {
  $stateProvider
    .state('home', {
      url: '/home',
      templateUrl: '/templates/home.html',
      controller: 'DashCtrl'
    })
    .state('food', {
      url: '/food',
      templateUrl: '/templates/food.html',
      controller: 'ListCtrl',
      resolve: {
        foodPromise: ['foods', function(foods) {
            return foods.getFood();
        }]
      }
    })
    .state('recipes', {
      url: '/recipes',
      templateUrl: '/templates/recipes.html',
      controller: 'ListCtrl',
      resolve: {
        recipePromise: ['recipes', function(recipes) {
            return recipes.getAll();
        }]
      }
    })
    .state('recipeform', {
      url: '/recipes/form',
      templateUrl: '/templates/recipeform.html',
      controller: 'ListCtrl'
    })
    .state('recipe', {
      url: '/recipes/{id}',
      templateUrl: '/templates/recipe.html',
      controller: 'RecipesCtrl',
      resolve: {
        recipe: ['$stateParams', 'recipes', function($stateParams, recipes) {
          return recipes.get($stateParams.id);
        }]
      }
    })
    .state('editRecipe', {
      url: '/recipes/{id}/edit',
      templateUrl: '/templates/recipeeditform.html',
      controller: 'RecipesCtrl',
      resolve: {
        recipe: ['$stateParams', 'recipes', function($stateParams, recipes) {
          return recipes.get($stateParams.id);
        }]
      }
    })
    .state('login', {
      url: '/login',
      templateUrl: '/templates/login.html',
      controller: 'AuthCtrl',
      onEnter: ['$state', 'auth', function($state, auth) {
        if(auth.isLoggedIn()) {
          $state.go('home');
        }
      }]
    })
    .state('register', {
      url: '/register',
      templateUrl: '/templates/register.html',
      controller: 'AuthCtrl',
      onEnter: ['$state', 'auth', function($state, auth) {
        if(auth.isLoggedIn()) {
          $state.go('home');
        }
      }]
    });

  $urlRouterProvider.otherwise('home');
}]);

// Factory for auth handling all authorization and
// user id related functions
app.factory('auth', ['$http', '$window', function($http, $window) {
  let auth = {};

  // save given token
  auth.saveToken = function(token) {
    $window.localStorage['time-crunch-token'] = token;
  };

  // get token from localStorage
  auth.getToken = function() {
    return $window.localStorage['time-crunch-token'];
  };

  // register user
  auth.register = function(user) {
    return $http.post('/register', user).then(function(data) {
      auth.saveToken(data.data.token);
    });
  };

  // login user save token
  auth.logIn = function(user) {
    return $http.post('/login', user).then(function(data) {
      auth.saveToken(data.data.token);
    });
  };

  // logout a user and delete token
  auth.logOut = function() {
    $window.localStorage.removeItem('time-crunch-token');
  };

  // check if user is logged in
  auth.isLoggedIn = function() {
    let token = auth.getToken();

    if(token) {
      let payload = JSON.parse($window.atob(token.split('.')[1]));

      return payload.exp > Date.now() / 1000;
    } else {
      return false;
    }
  };

  // returns currently logged in username
  auth.currentUser = function() {
    if(auth.isLoggedIn()) {
      let token = auth.getToken();
      let payload = JSON.parse($window.atob(token.split('.')[1]));

      return payload.username;
      }
    };

  return auth;
}]);

// Factory for foods handling all related functions
app.factory('foods', ['$http', '$state', 'auth', function($http, $state, auth) {
  let o = {
    foods: []
  };

  // Load food list for the current user
  o.getFood = function() {
    if(auth.isLoggedIn()) {
      return $http.get('/food', {
        headers: {Authorization: 'Bearer ' + auth.getToken()}
      }).then(function(data) {
        angular.copy(data.data, o.foods);
      });
    }
    $state.go('login');
  };

  // Add a food to a user's food list
  o.create = function(food) {
    return $http.post('/food', food, {
      headers: {Authorization: 'Bearer ' + auth.getToken()}
    }).then(function(data) {
      o.getFood();
    });
  };

  // Update a specific food
  o.update = function(food) {
    return $http.put('/food/' + food._id, food, {
      headers: {Authorization: 'Bearer ' + auth.getToken()}
    }).then(function(data) {
      o.getFood();
    });
  }

  // Remove a food from a user's food list
  o.delete = function(food) {
    return $http.delete('/food/' + food._id).then(function(data) {
      o.getFood();
    });
  };

  return o;
}]);

// Factory for recipes handling related functions
app.factory('recipes', ['$http', 'auth', function($http, auth) {
  let o = {
    recipes: []
  };

  // Get all recipes
  o.getAll = function() {
    if(auth.isLoggedIn()) {
      return $http.get('/recipes', {
        headers: {Authorization: 'Bearer ' + auth.getToken()}
      }).then(function(data) {
        angular.copy(data.data, o.recipes);
      });
    }
    return $http.get('/recipes/public').then(function(data) {
      angular.copy(data.data, o.recipes);
    });
  };

  // Get a specific recipe
  o.get = function(id) {
    return $http.get('/recipes/' + id).then(function(data) {
      return data.data;
    });
  };

  // Create a new recipe
  o.create = function(recipe) {
    return $http.post('/recipes', recipe, {
      headers: {Authorization: 'Bearer ' + auth.getToken()}
    }).then(function(data) {
      return data.data._id;
    });
  };

  // Update a recipe
  o.update = function(recipe) {
    return $http.put('/recipes/' + recipe._id, recipe, {
      headers: {Authorization: 'Bearer ' + auth.getToken()}
    }).then(function(data) {
      o.getAll();
    });
  };

  // Upvote a recipe
  o.upvote = function(recipe) {
    return $http.put('/recipes/' + recipe._id + '/upvote', null, {
      headers: {Authorization: 'Bearer ' + auth.getToken()}
    }).then(function(data) {
      recipe.score += 1;
    });
  };

  // Delete a recipe
  o.delete = function(recipe) {
    return $http.delete('/recipes/' + recipe._id).then(function(data) {
      o.getAll();
    });
  };

  return o;
}]);

// Factory for ingredients handling related functions
app.factory('ingredients', ['$http', function($http){
  let o = {};

  // Create a new ingredient
  o.create = function(recipeID, ingredient) {
    return $http.post('/recipes/' + recipeID + '/ingredients',
      ingredient).then(function(data) {
        return data.data;
    });
  };

  // Delete an ingredient
  o.delete = function(recipeID, ingredient) {
    return $http.delete('/recipes/' + recipeID + 'ingredients/' + ingredient._id);
  };

  return o;
}]);

// Controller for dashboard on home/index
app.controller('DashCtrl', [
  '$scope',
  'foods',
  function($scope, foods){
    $scope.foods = foods.foods;
}]);

// Controller for recipe and food lists
app.controller('ListCtrl', [
  '$scope',
  '$state',
  'auth',
  'ingredients',
  'foods',
  'recipes',
  function($scope, $state, auth, ingredients, foods, recipes) {

    $scope.foods = foods.foods;
    $scope.recipes = recipes.recipes;
    $scope.ingredients = [];
    $scope.isLoggedIn = auth.isLoggedIn;

    $scope.categories = [
      "Baking",
      "Dairy",
      "Fruit",
      "Herb/Spice",
      "Meat",
      "Sauce",
      "Vegetable",
      "Other-Ingredient",
      "Other-Food",
      "Other-Produce"
    ];

    $scope.sortBy = ["-score", "sellBy", "name"];
    $scope.filterCat = "";

    // Set containing ingredient names
    let ingNames = new Set();

    // Function used to filter food by category
    $scope.filter = function() {
      return function(item) {
        if($scope.filterCat == "" || !$scope.filterCat
          || $scope.filterCat == item.category) {
          return true;
        }
        return false;
      };
    };

    // Add a new food
    $scope.addFood = function() {
      // Make sure food name is valid
      if(!$scope.name || $scope.name == '') {
        alert("Please fill in the name field");
        return;
      }
      // If sell by was left blank, set it to 2 years from now
      if(!$scope.sellBy) {
        $scope.sellBy = new Date();
        $scope.sellBy.setDate($scope.sellBy.getDate() + 730);
      }
      foods.create({
        name: $scope.name,
        sellBy: $scope.sellBy,
        amount: $scope.amount,
        units: $scope.units,
        category: $scope.category
      });
    };

    // Update specific food
    $scope.updateFood = function(food) {
      // Make sure food name is valid
      if(!food.name || food.name == '') {
        alert("Please fill in the name field");
        return;
      }

      foods.update(food).then(function success() {
        // Reload food list
        $state.go('food');
      }, function failure(error) {
        // Show error message
        $scope.error = error.data;
      });
    }

    // Remove a food
    $scope.removeFood = function(food) {
      foods.delete(food);
    };

    // Add a new recipe
    $scope.addRecipe = function() {
      //Check that the recipe has a name, ingredients, and instructions
      if(!$scope.name || $scope.name == '' || !$scope.ingredients) {
        $scope.error = {
          message: "Please make sure all fields are filled out"
        };
        return;
      }

      if(!$scope.instructions && !$scope.link) {
        $scope.error= {
          message: "Please provide either instructions or a link to the full recipe"
        };
        return;
      }

      recipes.create({
        name: $scope.name,
        prepTime: $scope.prepTime,
        link: $scope.link,
        instructions: $scope.instructions,
        private: $scope.private
      }).then(function success(id) {
        // Return to the recipe list
        for(i = 0; i < $scope.ingredients.length; i++) {
          ingredients.create(id, $scope.ingredients[i]);
        }
        $scope.recipes = recipes.recipes;
        $state.go('recipes');
      }, function failure(error) {
        // Show error message
        $scope.error = error.data;
      });
    };

    // Upvote specific recipe
    $scope.upvoteRecipe = function(recipe) {
      recipes.upvote(recipe);
    };

    // Add an ingredient to a recipe
    $scope.addIngredient = function() {
      // Load existing ingredients into ingNames
      if(ingNames.size == 0 && $scope.ingredients.length > 0) {
        for(i = 0; i < $scope.ingredients.length; i++) {
          ingNames.add($scope.ing_name);
        }
      }
      // If ingredient already exists show alert
      if(ingNames.has($scope.ing_name)) {
        $scope.error = {
          message: "Duplicate ingredient, please change ingredient name"
        };
        return;
      }

      if($scope.ing_name) {
        ($scope.ingredients).push({
          name: $scope.ing_name,
          amount: $scope.ing_amount,
          unit: $scope.ing_unit
        });
      }
      else {
        $scope.error = {
          message: "Missing ingredient name"
        };
      }

      // Add new ingredient name to the set
      ingNames.add($scope.ing_name);
    };

    // Remove an ingredient from a recipe
    $scope.removeIngredient = function(ingredient) {
      if($scope.ingredients.length == 1) {
        $scope.ingredients = [];
      }
      else {
        let i = $scope.ingredients.indexOf(ingredient);
        // Remove ingredient name from set
        ingNames.delete(ingredient.name);
        $scope.ingredients.splice(i, 1);
      }
    };
}]);

// Controller for recipe details page and edit form
app.controller('RecipesCtrl', [
  '$scope',
  '$state',
  'ingredients',
  'recipe',
  'recipes',
  'auth',
  function($scope, $state, ingredients, recipe, recipes, auth){
    $scope.recipe = recipe;
    // Set containing ingredient names
    let ingNames = new Set();

    // Determines if a user is allowed to edit this
    // recipe
    $scope.canEdit = function() {
      return recipe.author == auth.currentUser();
    };

    // Remove an ingredient from a recipe
    $scope.removeIngredient = function(ingredient) {
      if($scope.recipe.ingredients.length == 1) {
        $scope.recipe.ingredients = [];
        ingNames.clear();
      }
      else {
        let i = $scope.recipe.ingredients.indexOf(ingredient);
        // Remove ingredient name from set
        ingNames.delete(ingredient.name);
        $scope.recipe.ingredients.splice(i, 1);
      }
    };

    // Add an ingredient to a recipe
    $scope.addIngredient = function() {
      // Load existing ingredients into ingNames
      if(ingNames.size < $scope.recipe.ingredients.length) {
        for(i = 0; i < $scope.recipe.ingredients.length; i++) {
          ingNames.add($scope.ing_name);
        }
      }
      // If ingredient already exists show alert
      if(ingNames.has($scope.ing_name)) {
        $scope.error = {
          message: "Duplicate ingredient, please change ingredient name"
        };
        return;
      }
      // Build the ingredient string
      let ing = {
        name: $scope.ing_name,
        amount: $scope.ing_amount,
        unit: $scope.ing_unit
      };
      if(ing.name) {
        ($scope.recipe.ingredients).push(ing);
        // Add new ingredient name to the set
        ingNames.add(ing.name);
      }
      else {
        $scope.error = {
          message: "Missing ingredient name"
        };
      }
    };

    // Edit an existing recipe
    $scope.editRecipe = function() {
      for(i = 0; i < $scope.recipe.ingredients.length; i++) {
        if(!$scope.recipe.ingredients[i]._id) {
          ingredients.create($scope.recipe._id, $scope.recipe.ingredients[i]);
        }
      }
      recipes.update($scope.recipe).then(function success() {
        // Return to the recipe list
        $state.go('recipe', {id: recipe._id});
      }, function failure(error) {
        // Show error message
        $scope.error = error.data;
      });
    };

    // Upvote this recipe
    $scope.upvoteRecipe = function() {
      recipes.upvote($scope.recipe);
      $scope.recipe.score += 1;
      $scope.voted = true;
    };

    // Deletes the recipe
    $scope.deleteRecipe = function() {
      // Make sure user is authorized
      if($scope.canEdit()) {
        recipes.delete(recipe);
        $state.go('recipes');
      }
      else {
        // If not allowed show an alert
        alert('You cannot delete this recipe!');
      }
    };

}]);

// Controller for logging in and registering
app.controller('AuthCtrl', [
  '$scope',
  '$state',
  'auth',
  function($scope, $state, auth) {
    $scope.user = {};

    // Registers a new user
    $scope.register = function() {
      auth.register($scope.user).then(function success() {
        // Go to dashboard once logged in
        $state.go('home');
      }, function failure(error) {
        // Show error message
        $scope.error = error.data;
      });
    };

    // Starts a new login session
    $scope.logIn = function() {
      auth.logIn($scope.user).then(function success() {
        // Go to dashboard once logged in
        $state.go('home');
      }, function failure(error) {
        // Show error message
        $scope.error = error.data;
      });
    };
}]);

// Controller for dashboard on home/index
app.controller('NavCtrl', [
  '$scope',
  'auth',
  function($scope, auth){

    $scope.isLoggedIn = auth.isLoggedIn;
    $scope.currentUser = auth.currentUser;
    $scope.logOut = auth.logOut;
}]);<|endoftext|>


<commit_before>// jQuery
var $renderPort = $('#renderPort');

// SocketIO
var socket = io();
var connected = false;

// Phone variables
var vX = 0, vY = 0, vZ = 0,
  sX = 0, sY = 0, sZ = 0,
  alpha = 0, beta = 0, gamma = 0;

// 3D Scene variables
var scene, camera, renderer, cube;
var ambientlight, hemispherelight, dirlight, pointlight;

// Setup 3D scene
function setupScene() {
  // Scene object
  scene = new THREE.Scene();
  camera = new THREE.PerspectiveCamera( 75, $renderPort.width()/$renderPort.height(), 0.1, 10000);

  // Create renderer
  renderer = new THREE.WebGLRenderer( { alpha: true } );
  renderer.setSize( $renderPort.width(), $renderPort.height());

  // Light
  ambientlight = new THREE.AmbientLight( 0x404040 );
  scene.add( ambientlight );

  dirLight = new THREE.DirectionalLight(0xffffff, 1);
  dirLight.position.set(400, 500, 50);
  scene.add(dirLight);

  hemispherelight = new THREE.HemisphereLight( 0xffffbb, 0x080820, 1 );
  hemispherelight.position.set(0, 500, 0);
  hemispherelight.color.setHSL( 0.6, 1, 0.6 );
  hemispherelight.groundColor.setHSL( 0.095, 1, 0.75 );
  scene.add( hemispherelight );

  // Add renderer
  $renderPort.append( renderer.domElement );

  // Cam position
  camera.position.z = 900;
  camera.position.y = 900;
  camera.position.x = 900;
  camera.up = new THREE.Vector3( 0, 1, 0 );

  scene.add( new THREE.AxisHelper( 600 ));

  // Add cube
  addCube();

  camera.lookAt( cube.position );
  scene.add( camera );
}

function addCube() {
  var geometry = new THREE.BoxGeometry( 300,300,300 );
  var material = new THREE.MeshPhongMaterial( { color: 0x47d1d1 });
  cube = new THREE.Mesh( geometry, material );
  cube.castShadow = true;
  scene.add( cube );
}

var render = function () {
  // cube.rotation.x += gamma*2;
  cube.rotation.y += gamma*2;
  cube.rotation.z += alpha*2;

  requestAnimationFrame( render );
  renderer.render( scene, camera );
}

window.addEventListener('resize', function() {
  renderer.setSize( $renderPort.width(), $renderPort.height());
  camera.aspect = $renderPort.width()/$renderPort.height();
  camera.updateProjectionMatrix();
});

// Kickstart
setupScene();
render();

// Check connection
socket.on('connection', function() {
  connected = true;
});

// If client has accelerometer and gyro data
if (window.DeviceMotionEvent) {
  window.addEventListener('devicemotion', function (data) {
    var acc = data.accelerationIncludingGravity;
    var vel = { vX: vX, vY: vY, vZ: vZ };
    var disp = { sX: sX, sY: sY, sZ: sZ };

    var phoneData = {
      accelerometer: acc,
      velocity: vel,
      displacement: disp,
      rotationRate: data.rotationRate,
      interval: data.interval
    };

    // Tell server
    socket.emit('phone-data', phoneData);
  }, false);
}

socket.on('phone-data', function (data) {
    vX = data.velocity.vX;
    vY = data.velocity.vY;
    vZ = data.velocity.vZ;

    sX = data.displacement.sX;
    sY = data.displacement.sY;
    sZ = data.displacement.sZ;

  // Check and update angle
  if (aboveAbsThreshold(data.rotationRate.alpha, 0.1) || aboveAbsThreshold(data.rotationRate.beta, 0.1) || aboveAbsThreshold(data.rotationRate.gamma, 0.1)) {
    var scalar = 100;
    alpha = data.rotationRate.alpha*data.interval/scalar;
    beta = data.rotationRate.beta*data.interval/scalar;
    gamma = data.rotationRate.gamma*data.interval/scalar;
  }

});
function aboveAbsThreshold(input, threshold) {
  return Math.abs(input) > threshold ? true : false;
}<commit_msg>Fixing axes.<commit_after>// jQuery
var $renderPort = $('#renderPort');

// SocketIO
var socket = io();
var connected = false;

// Phone variables
var vX = 0, vY = 0, vZ = 0,
  sX = 0, sY = 0, sZ = 0,
  alpha = 0, beta = 0, gamma = 0;

// 3D Scene variables
var scene, camera, renderer, cube;
var ambientlight, hemispherelight, dirlight, pointlight;

// Setup 3D scene
function setupScene() {
  // Scene object
  scene = new THREE.Scene();
  camera = new THREE.PerspectiveCamera( 75, $renderPort.width()/$renderPort.height(), 0.1, 10000);

  // Create renderer
  renderer = new THREE.WebGLRenderer( { alpha: true } );
  renderer.setSize( $renderPort.width(), $renderPort.height());

  // Light
  ambientlight = new THREE.AmbientLight( 0x404040 );
  scene.add( ambientlight );

  dirLight = new THREE.DirectionalLight(0xffffff, 1);
  dirLight.position.set(400, 500, 50);
  scene.add(dirLight);

  hemispherelight = new THREE.HemisphereLight( 0xffffbb, 0x080820, 1 );
  hemispherelight.position.set(0, 500, 0);
  hemispherelight.color.setHSL( 0.6, 1, 0.6 );
  hemispherelight.groundColor.setHSL( 0.095, 1, 0.75 );
  scene.add( hemispherelight );

  // Add renderer
  $renderPort.append( renderer.domElement );

  // Cam position
  camera.position.z = 900;
  camera.position.y = 0;
  camera.position.x = 0;
  camera.up = new THREE.Vector3( 0, 1, 0 );

  scene.add( new THREE.AxisHelper( 600 ));

  // Add cube
  addCube();

  camera.lookAt( cube.position );
  scene.add( camera );
}

function addCube() {
  var geometry = new THREE.BoxGeometry( 300,300,300 );
  var material = new THREE.MeshPhongMaterial( { color: 0x47d1d1 });
  cube = new THREE.Mesh( geometry, material );
  cube.castShadow = true;
  scene.add( cube );
}

var render = function () {
  // cube.rotation.x += gamma*2;
  cube.rotation.y += gamma*2;
  cube.rotation.z += alpha*2;

  requestAnimationFrame( render );
  renderer.render( scene, camera );
}

window.addEventListener('resize', function() {
  renderer.setSize( $renderPort.width(), $renderPort.height());
  camera.aspect = $renderPort.width()/$renderPort.height();
  camera.updateProjectionMatrix();
});

// Kickstart
setupScene();
render();

// Check connection
socket.on('connection', function() {
  connected = true;
});

// If client has accelerometer and gyro data
if (window.DeviceMotionEvent) {
  window.addEventListener('devicemotion', function (data) {
    var acc = data.accelerationIncludingGravity;
    var vel = { vX: vX, vY: vY, vZ: vZ };
    var disp = { sX: sX, sY: sY, sZ: sZ };

    var phoneData = {
      accelerometer: acc,
      velocity: vel,
      displacement: disp,
      rotationRate: data.rotationRate,
      interval: data.interval
    };

    // Tell server
    socket.emit('phone-data', phoneData);
  }, false);
}

socket.on('phone-data', function (data) {
    vX = data.velocity.vX;
    vY = data.velocity.vY;
    vZ = data.velocity.vZ;

    sX = data.displacement.sX;
    sY = data.displacement.sY;
    sZ = data.displacement.sZ;

  // Check and update angle
  if (aboveAbsThreshold(data.rotationRate.alpha, 0.1) || aboveAbsThreshold(data.rotationRate.beta, 0.1) || aboveAbsThreshold(data.rotationRate.gamma, 0.1)) {
    var scalar = 100;
    alpha = data.rotationRate.alpha*data.interval/scalar;
    beta = data.rotationRate.beta*data.interval/scalar;
    gamma = data.rotationRate.gamma*data.interval/scalar;
  }

});
function aboveAbsThreshold(input, threshold) {
  return Math.abs(input) > threshold ? true : false;
}<|endoftext|>


<commit_before>(function(){
    if(typeof EthereumProvider === "undefined"){
    var callbackId = 0;
    var callbacks = {};

    var bridgeSend = function (data) {
        ReactNativeWebView.postMessage(JSON.stringify(data));
    }

    var history = window.history;
    var pushState = history.pushState;
    history.pushState = function(state) {
        setTimeout(function () {
            bridgeSend({
               type: 'history-state-changed',
               navState: { url: location.href, title: document.title }
            });
        }, 100);
        return pushState.apply(history, arguments);
    };

    function sendAPIrequest(permission, params) {
        var messageId = callbackId++;
        var params = params || {};

        bridgeSend({
            type: 'api-request',
            permission: permission,
            messageId: messageId,
            params: params
        });

        return new Promise(function (resolve, reject) {
            params['resolve'] = resolve;
            params['reject'] = reject;
            callbacks[messageId] = params;
        });
    }

    function qrCodeResponse(data, callback){
        var result = data.data;
        var regex = new RegExp(callback.regex);
        if (!result) {
            if (callback.reject) {
                callback.reject(new Error("Cancelled"));
            }
        }
        else if (regex.test(result)) {
            if (callback.resolve) {
                callback.resolve(result);
            }
        } else {
            if (callback.reject) {
                callback.reject(new Error("Doesn't match"));
            }
        }
    }

    function Unauthorized() {
      this.name = "Unauthorized";
      this.id = 4100;
      this.code = 4100;
      this.message = "The requested method and/or account has not been authorized by the user.";
    }
    Unauthorized.prototype = Object.create(Error.prototype);

    function UserRejectedRequest() {
      this.name = "UserRejectedRequest";
      this.id = 4001;
      this.code = 4001;
      this.message = "The user rejected the request.";
    }
    UserRejectedRequest.prototype = Object.create(Error.prototype);
    ReactNativeWebView.onMessage = function (message)
    {
        data = JSON.parse(message);
        var id = data.messageId;
        var callback = callbacks[id];

        if (callback) {
            if (data.type === "api-response") {
                if (data.permission == 'qr-code'){
                    qrCodeResponse(data, callback);
                } else if (data.isAllowed) {
                    if (data.permission == 'web3') {
                        window.statusAppcurrentAccountAddress = data.data[0];
                        window.ethereum.emit("accountsChanged", data.data);
                    }
                    callback.resolve(data.data);
                } else {
                    callback.reject(new UserRejectedRequest());
                }
            }
            else if (data.type === "web3-send-async-callback")
            {
                if (callback.beta)
                {
                    if (data.error)
                    {
                        if (data.error.code == 4100)
                            callback.reject(new Unauthorized());
                        else
                            callback.reject(data.error);
                    }
                    else
                    {
                        callback.resolve(data.result.result);
                    }
                }
                else if (callback.results)
                {
                    callback.results.push(data.error || data.result);
                    if (callback.results.length == callback.num)
                        callback.callback(undefined, callback.results);
                }
                else
                {
                    callback.callback(data.error, data.result);
                }
            }
        }
    };

    function web3Response (payload, result){
        return {id: payload.id,
                jsonrpc: "2.0",
                result: result};
    }

    function getSyncResponse (payload) {
        if (payload.method == "eth_accounts" && (typeof window.statusAppcurrentAccountAddress !== "undefined")) {
            return web3Response(payload, [window.statusAppcurrentAccountAddress])
        } else if (payload.method == "eth_coinbase" && (typeof window.statusAppcurrentAccountAddress !== "undefined")) {
            return web3Response(payload, window.statusAppcurrentAccountAddress)
        } else if (payload.method == "net_version" || payload.method == "eth_chainId"){
            return web3Response(payload, window.statusAppNetworkId)
        } else if (payload.method == "eth_uninstallFilter"){
            return web3Response(payload, true);
        } else {
            return null;
        }
    }

    var StatusAPI = function () {};

    StatusAPI.prototype.getContactCode = function () {
        return sendAPIrequest('contact-code');
    };

    var EthereumProvider = function () {};

    EthereumProvider.prototype.isStatus = true;
    EthereumProvider.prototype.status = new StatusAPI();
    EthereumProvider.prototype.isConnected = function () { return true; };

    EthereumProvider.prototype._events = {};

    EthereumProvider.prototype.on = function(name, listener) {
        if (!this._events[name]) {
          this._events[name] = [];
        }
        this._events[name].push(listener);
    }

    EthereumProvider.prototype.removeListener = function (name, listenerToRemove) {
        if (!this._events[name]) {
          return
        }

        const filterListeners = (listener) => listener !== listenerToRemove;
        this._events[name] = this._events[name].filter(filterListeners);
    }

    EthereumProvider.prototype.emit = function (name, data) {
        if (!this._events[name]) {
          return
        }
        this._events[name].forEach(cb => cb(data));
    }
    EthereumProvider.prototype.enable = function () {
        if (window.statusAppDebug) { console.log("enable"); }
        return sendAPIrequest('web3');
    };

    EthereumProvider.prototype.scanQRCode = function (regex) {
        return sendAPIrequest('qr-code', {regex: regex});
    };

    EthereumProvider.prototype.request = function (requestArguments)
    {
         if (window.statusAppDebug) { console.log("request: " + JSON.stringify(requestArguments)); }
         if (!requestArguments) {
           return new Error('Request is not valid.');
         }
         var method = requestArguments.method;

         if (!method) {
           return new Error('Request is not valid.');
         }

         //Support for legacy send method
         if (typeof method !== 'string') {
           return this.sendSync(method);
         }

         if (method == 'eth_requestAccounts'){
             return sendAPIrequest('web3');
         }

         var syncResponse = getSyncResponse({method: method});
         if (syncResponse){
             return new Promise(function (resolve, reject) {
                                        resolve(syncResponse.result);
                                    });
         }

         var messageId = callbackId++;
         var payload = {id:      messageId,
                        jsonrpc: "2.0",
                        method:  method,
                        params:  requestArguments.params};

         bridgeSend({type:      'web3-send-async-read-only',
                     messageId: messageId,
                     payload:   payload});

         return new Promise(function (resolve, reject) {
                                callbacks[messageId] = {beta:    true,
                                                        resolve: resolve,
                                                        reject:  reject};
                            });
    };

    // (DEPRECATED) Support for legacy send method
    EthereumProvider.prototype.send = function (method, params = [])
    {
        if (window.statusAppDebug) { console.log("send (legacy): " + method);}
        return this.request({method: method, params: params});
    }

    // (DEPRECATED) Support for legacy sendSync method
    EthereumProvider.prototype.sendSync = function (payload)
    {
        if (window.statusAppDebug) { console.log("sendSync (legacy)" + JSON.stringify(payload));}
        if (payload.method == "eth_uninstallFilter"){
            this.sendAsync(payload, function (res, err) {})
        }
        var syncResponse = getSyncResponse(payload);
        if (syncResponse){
            return syncResponse;
        } else {
            return web3Response(payload, null);
        }
    };

    // (DEPRECATED) Support for legacy sendAsync method
    EthereumProvider.prototype.sendAsync = function (payload, callback)
    {
      if (window.statusAppDebug) { console.log("sendAsync (legacy)" + JSON.stringify(payload));}
      if (!payload) {
          return new Error('Request is not valid.');
      }
      if (payload.method == 'eth_requestAccounts'){
          return sendAPIrequest('web3');
      }
      var syncResponse = getSyncResponse(payload);
      if (syncResponse && callback) {
          callback(null, syncResponse);
      }
      else
      {
          var messageId = callbackId++;

          if (Array.isArray(payload))
          {
              callbacks[messageId] = {num:      payload.length,
                                      results:  [],
                                      callback: callback};
              for (var i in payload) {
                  bridgeSend({type:      'web3-send-async-read-only',
                              messageId: messageId,
                              payload:   payload[i]});
              }
          }
          else
          {
              callbacks[messageId] = {callback: callback};
              bridgeSend({type:      'web3-send-async-read-only',
                          messageId: messageId,
                          payload:   payload});
          }
      }
    };
    }

    window.ethereum = new EthereumProvider();
})();
<commit_msg>set depreacted metamask fields<commit_after>(function(){
    if(typeof EthereumProvider === "undefined"){
    var callbackId = 0;
    var callbacks = {};

    var bridgeSend = function (data) {
        ReactNativeWebView.postMessage(JSON.stringify(data));
    }

    var history = window.history;
    var pushState = history.pushState;
    history.pushState = function(state) {
        setTimeout(function () {
            bridgeSend({
               type: 'history-state-changed',
               navState: { url: location.href, title: document.title }
            });
        }, 100);
        return pushState.apply(history, arguments);
    };

    function sendAPIrequest(permission, params) {
        var messageId = callbackId++;
        var params = params || {};

        bridgeSend({
            type: 'api-request',
            permission: permission,
            messageId: messageId,
            params: params
        });

        return new Promise(function (resolve, reject) {
            params['resolve'] = resolve;
            params['reject'] = reject;
            callbacks[messageId] = params;
        });
    }

    function qrCodeResponse(data, callback){
        var result = data.data;
        var regex = new RegExp(callback.regex);
        if (!result) {
            if (callback.reject) {
                callback.reject(new Error("Cancelled"));
            }
        }
        else if (regex.test(result)) {
            if (callback.resolve) {
                callback.resolve(result);
            }
        } else {
            if (callback.reject) {
                callback.reject(new Error("Doesn't match"));
            }
        }
    }

    function Unauthorized() {
      this.name = "Unauthorized";
      this.id = 4100;
      this.code = 4100;
      this.message = "The requested method and/or account has not been authorized by the user.";
    }
    Unauthorized.prototype = Object.create(Error.prototype);

    function UserRejectedRequest() {
      this.name = "UserRejectedRequest";
      this.id = 4001;
      this.code = 4001;
      this.message = "The user rejected the request.";
    }
    UserRejectedRequest.prototype = Object.create(Error.prototype);
    ReactNativeWebView.onMessage = function (message)
    {
        data = JSON.parse(message);
        var id = data.messageId;
        var callback = callbacks[id];

        if (callback) {
            if (data.type === "api-response") {
                if (data.permission == 'qr-code'){
                    qrCodeResponse(data, callback);
                } else if (data.isAllowed) {
                    if (data.permission == 'web3') {
                        var selectedAddress = data.data[0]
                        window.statusAppcurrentAccountAddress = selectedAddress;
                        // Set deprecated metamask fields
                        window.ethereum.selectedAddress = selectedAddress;
                        window.ethereum.emit("accountsChanged", data.data);
                    }
                    callback.resolve(data.data);
                } else {
                    callback.reject(new UserRejectedRequest());
                }
            }
            else if (data.type === "web3-send-async-callback")
            {
                if (callback.beta)
                {
                    if (data.error)
                    {
                        if (data.error.code == 4100)
                            callback.reject(new Unauthorized());
                        else
                            callback.reject(data.error);
                    }
                    else
                    {
                        callback.resolve(data.result.result);
                    }
                }
                else if (callback.results)
                {
                    callback.results.push(data.error || data.result);
                    if (callback.results.length == callback.num)
                        callback.callback(undefined, callback.results);
                }
                else
                {
                    callback.callback(data.error, data.result);
                }
            }
        }
    };

    function web3Response (payload, result){
        return {id: payload.id,
                jsonrpc: "2.0",
                result: result};
    }

    function getSyncResponse (payload) {
        if (payload.method == "eth_accounts" && (typeof window.statusAppcurrentAccountAddress !== "undefined")) {
            return web3Response(payload, [window.statusAppcurrentAccountAddress])
        } else if (payload.method == "eth_coinbase" && (typeof window.statusAppcurrentAccountAddress !== "undefined")) {
            return web3Response(payload, window.statusAppcurrentAccountAddress)
        } else if (payload.method == "net_version" || payload.method == "eth_chainId"){
            return web3Response(payload, window.statusAppNetworkId)
        } else if (payload.method == "eth_uninstallFilter"){
            return web3Response(payload, true);
        } else {
            return null;
        }
    }

    var StatusAPI = function () {};

    StatusAPI.prototype.getContactCode = function () {
        return sendAPIrequest('contact-code');
    };

    var EthereumProvider = function () {};

    EthereumProvider.prototype.isStatus = true;
    EthereumProvider.prototype.status = new StatusAPI();
    EthereumProvider.prototype.isConnected = function () { return true; };
    // Set legacy metamask fields https://docs.metamask.io/guide/ethereum-provider.html#legacy-api
    EthereumProvider.prototype.networkVersion = window.statusAppNetworkId;
    EthereumProvider.prototype.chainId = "0x" + window.statusAppNetworkId.toString(16);

    EthereumProvider.prototype._events = {};

    EthereumProvider.prototype.on = function(name, listener) {
        if (!this._events[name]) {
          this._events[name] = [];
        }
        this._events[name].push(listener);
    }

    EthereumProvider.prototype.removeListener = function (name, listenerToRemove) {
        if (!this._events[name]) {
          return
        }

        const filterListeners = (listener) => listener !== listenerToRemove;
        this._events[name] = this._events[name].filter(filterListeners);
    }

    EthereumProvider.prototype.emit = function (name, data) {
        if (!this._events[name]) {
          return
        }
        this._events[name].forEach(cb => cb(data));
    }
    EthereumProvider.prototype.enable = function () {
        if (window.statusAppDebug) { console.log("enable"); }
        return sendAPIrequest('web3');
    };

    EthereumProvider.prototype.scanQRCode = function (regex) {
        return sendAPIrequest('qr-code', {regex: regex});
    };

    EthereumProvider.prototype.request = function (requestArguments)
    {
         if (window.statusAppDebug) { console.log("request: " + JSON.stringify(requestArguments)); }
         if (!requestArguments) {
           return new Error('Request is not valid.');
         }
         var method = requestArguments.method;

         if (!method) {
           return new Error('Request is not valid.');
         }

         //Support for legacy send method
         if (typeof method !== 'string') {
           return this.sendSync(method);
         }

         if (method == 'eth_requestAccounts'){
             return sendAPIrequest('web3');
         }

         var syncResponse = getSyncResponse({method: method});
         if (syncResponse){
             return new Promise(function (resolve, reject) {
                                        resolve(syncResponse.result);
                                    });
         }

         var messageId = callbackId++;
         var payload = {id:      messageId,
                        jsonrpc: "2.0",
                        method:  method,
                        params:  requestArguments.params};

         bridgeSend({type:      'web3-send-async-read-only',
                     messageId: messageId,
                     payload:   payload});

         return new Promise(function (resolve, reject) {
                                callbacks[messageId] = {beta:    true,
                                                        resolve: resolve,
                                                        reject:  reject};
                            });
    };

    // (DEPRECATED) Support for legacy send method
    EthereumProvider.prototype.send = function (method, params = [])
    {
        if (window.statusAppDebug) { console.log("send (legacy): " + method);}
        return this.request({method: method, params: params});
    }

    // (DEPRECATED) Support for legacy sendSync method
    EthereumProvider.prototype.sendSync = function (payload)
    {
        if (window.statusAppDebug) { console.log("sendSync (legacy)" + JSON.stringify(payload));}
        if (payload.method == "eth_uninstallFilter"){
            this.sendAsync(payload, function (res, err) {})
        }
        var syncResponse = getSyncResponse(payload);
        if (syncResponse){
            return syncResponse;
        } else {
            return web3Response(payload, null);
        }
    };

    // (DEPRECATED) Support for legacy sendAsync method
    EthereumProvider.prototype.sendAsync = function (payload, callback)
    {
      if (window.statusAppDebug) { console.log("sendAsync (legacy)" + JSON.stringify(payload));}
      if (!payload) {
          return new Error('Request is not valid.');
      }
      if (payload.method == 'eth_requestAccounts'){
          return sendAPIrequest('web3');
      }
      var syncResponse = getSyncResponse(payload);
      if (syncResponse && callback) {
          callback(null, syncResponse);
      }
      else
      {
          var messageId = callbackId++;

          if (Array.isArray(payload))
          {
              callbacks[messageId] = {num:      payload.length,
                                      results:  [],
                                      callback: callback};
              for (var i in payload) {
                  bridgeSend({type:      'web3-send-async-read-only',
                              messageId: messageId,
                              payload:   payload[i]});
              }
          }
          else
          {
              callbacks[messageId] = {callback: callback};
              bridgeSend({type:      'web3-send-async-read-only',
                          messageId: messageId,
                          payload:   payload});
          }
      }
    };
    }

    window.ethereum = new EthereumProvider();
})();
<|endoftext|>


<commit_before>var DigitalGazette = {
    Sidebar: {
        ToggleButton: {
            div: document.createElement('div'),
            width: 16, // px
            setup: function() {
                this.div.setAttribute('id', 'dg_sidebar_toggle');
                this.div.style['position'] = 'absolute';
                this.div.style['top'] = '0';
                this.div.style['left'] = '0';
                this.div.style['height'] = '100%';
                this.div.style['width'] = this.width + 'px';
                this.div.style['background'] = 'red'; // TODO: remove this.
                this.div.onclick = function() { DigitalGazette.Sidebar.toggle(); };
            },
        },
        div: document.createElement('div'),
        wrapper: document.createElement('div'),
        setup: function() {
            Element.extend(this.wrapper);
            Element.extend(this.div);
            this.div.setAttribute('id', 'dg_sidebar');
            this.wrapper.style['position'] = 'absolute';
            this.wrapper.style['top'] = '160px';
            this.wrapper.style['right'] = '0';
            this.div.style['height'] = '100%';
            this.div.style['width'] = '100%';
            this.div.style['padding-left'] = this.ToggleButton.width * 1.5;
            this.div.style['background'] = 'blue'; // TODO: remove this.
            this.ToggleButton.setup();
            this.wrapper.appendChild(this.ToggleButton.div);
            this.wrapper.appendChild(this.div);
            document.body.appendChild(this.wrapper);
            this.adjust();
            var self = this;
            window.onresize = function() { self.adjust(); };
        },
        adjust: function() {
            this.wrapper.style['height'] = (document.height - Element.positionedOffset(this.wrapper)[1] - $('footer_wrapper').getHeight()) + 'px';
            if(this.visible())
                this.wrapper.style['width'] = ((window.innerWidth / 100) * 25) + 'px';
            else
                this.wrapper.style['width'] = this.ToggleButton.width + 'px';
        },
        insert: function() {
            for(i in arguments) {
                this.div.innerHTML += arguments[i] + "\n";
            }
        },
        toggle: function() {
            this.div.toggle();
            this.adjust();
        },
        visible: function() {
            return (this.div.style['display'] != 'none');
        }
    }
};

window.onload = function() { DigitalGazette.Sidebar.setup(); };
<commit_msg>also adjusting sidebar on document resize<commit_after>var DigitalGazette = {
    Sidebar: {
        ToggleButton: {
            div: document.createElement('div'),
            width: 16, // px
            setup: function() {
                this.div.setAttribute('id', 'dg_sidebar_toggle');
                this.div.style['position'] = 'absolute';
                this.div.style['top'] = '0';
                this.div.style['left'] = '0';
                this.div.style['height'] = '100%';
                this.div.style['width'] = this.width + 'px';
                this.div.style['background'] = 'red'; // TODO: remove this.
                this.div.onclick = function() { DigitalGazette.Sidebar.toggle(); };
            },
        },
        div: document.createElement('div'),
        wrapper: document.createElement('div'),
        setup: function() {
            Element.extend(this.wrapper);
            Element.extend(this.div);
            this.div.setAttribute('id', 'dg_sidebar');
            this.wrapper.style['position'] = 'absolute';
            this.wrapper.style['top'] = '160px';
            this.wrapper.style['right'] = '0';
            this.div.style['height'] = '100%';
            this.div.style['width'] = '100%';
            this.div.style['padding-left'] = this.ToggleButton.width * 1.5;
            this.div.style['background'] = 'blue'; // TODO: remove this.
            this.ToggleButton.setup();
            this.wrapper.appendChild(this.ToggleButton.div);
            this.wrapper.appendChild(this.div);
            document.body.appendChild(this.wrapper);
            this.adjust();
            var self = this;
            window.onresize = function() { self.adjust(); };
            document.onresize = function() { self.adjust(); };
        },
        adjust: function() {
            this.wrapper.style['height'] = (document.height - Element.positionedOffset(this.wrapper)[1] - $('footer_wrapper').getHeight()) + 'px';
            if(this.visible())
                this.wrapper.style['width'] = ((window.innerWidth / 100) * 25) + 'px';
            else
                this.wrapper.style['width'] = this.ToggleButton.width + 'px';
        },
        insert: function() {
            for(i in arguments) {
                this.div.innerHTML += arguments[i] + "\n";
            }
        },
        toggle: function() {
            this.div.toggle();
            this.adjust();
        },
        visible: function() {
            return (this.div.style['display'] != 'none');
        }
    }
};

window.onload = function() { DigitalGazette.Sidebar.setup(); };
<|endoftext|>


<commit_before>import L from 'leaflet'
import 'leaflet/dist/leaflet.css!'
import 'leaflet-loading'
import 'leaflet-loading/src/Control.Loading.css!'
import 'leaflet-groupedlayercontrol'
import 'leaflet-groupedlayercontrol/dist/leaflet.groupedlayercontrol.min.css!'
import {$} from 'minified'

import * as CovJSON from 'covjson-reader'
import * as RestAPI from 'coverage-rest-client'
import LayerFactory from 'leaflet-coverage'

import Legend from 'leaflet-coverage/controls/Legend.js'
import TimeAxis from 'leaflet-coverage/controls/TimeAxis.js'
import ProfilePlot from 'leaflet-coverage/popups/VerticalProfilePlot.js'
import ParameterSync from 'leaflet-coverage/renderers/ParameterSync.js'
import * as palettes from 'leaflet-coverage/renderers/palettes.js'
import * as transform from 'leaflet-coverage/util/transform.js'
import {inject} from 'leaflet-coverage/controls/utils.js'

import UrlInput from './control.UrlInput.js'
import JSONInput from './control.JSONInput.js'

import './style.css!'

let map = L.map('map', {
  loadingControl: true,
  // initial center and zoom has to be set before layers can be added
  center: [10, 0],
  zoom: 2
})
window.map = map
let baseLayers = {
  'OSM':
    L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
       attribution: 'Map data © OpenStreetMap'
    })
}
baseLayers['OSM'].addTo(map)

let layerControl = L.control.groupedLayers(baseLayers, [], {collapsed: false}).addTo(map)

let layerFactory = LayerFactory()

// some initial test data to play with
let covs = ['coverages/trajectory.covjson',
            'coverages/grid.covjson',
            'coverages/grid-categorical.covjson',
            'coverages/profile.covjson',
            'coverages/profile-collection.covjson',
            'coverages/multipolygon.covjson'
           ]

// We use ParameterSync here so that multiple coverage layers that display the same
// parameter get synchronized in terms of their palette and extent.
// It also allows us to display a single legend only.
// Layers that don't have a single parameter get ignored automatically.
let paramSync = new ParameterSync({
  syncProperties: {
    palette: (p1, p2) => p1,
    paletteExtent: (e1, e2) => e1 && e2 ? [Math.min(e1[0], e2[0]), Math.max(e1[1], e2[1])] : null
  }
}).on('parameterAdd', e => {
    // The virtual sync layer proxies the synced palette, paletteExtent, and parameter.
    // The sync layer will fire a 'remove' event if all real layers for that parameter were removed.
    let layer = e.syncLayer
    if (layer.palette) {
      Legend(layer, {
        position: 'bottomright'
      }).addTo(map)
    }
  })

let layersOnMap = new Set()

function loadCov (url, group=undefined) {
  if (!group) {
    group = url
  }
  map.fire('dataloading')
  CovJSON.read(url)
    .then(cov => RestAPI.wrap(cov, {loader: CovJSON.read}))
    .then(cov => {

    map.fire('dataload')
    console.log('Coverage loaded: ', cov)

    // add each parameter as a layer

    if (cov.coverages) {
      // collection
      if (!cov.parameters) {
        throw new Error('Only coverage collections with a "parameters" property are supported')
      }
      for (let key of cov.parameters.keys()) {
        let opts = {keys: [key]}

        let layers = cov.coverages.map(coverage => createLayer(coverage, opts, true))
        let layer = L.layerGroup(layers)
        layerControl.addOverlay(layer, key, group)
      }
    } else {
      // single coverage
      for (let key of cov.parameters.keys()) {
        let opts = {keys: [key]}
        let layer = createLayer(cov, opts)

        // TODO use jsonld to properly query graph (together with using cov.id as reference point)
        if (cov.ld.inCollection) {
          group += '
(part of collection)'
        }
        layerControl.addOverlay(layer, key, group)
      }
    }
  }).catch(e => {
    map.fire('dataload')
    console.log(e)
    window.alert(e)
  })
}

function createLayer(cov, opts, nozoom) {
  let layer = layerFactory(cov, opts).on('add', e => {
    let covLayer = e.target
    console.log('layer added:', covLayer)

    // This registers the layer with the sync manager.
    // By doing that, the palette and extent get unified (if existing)
    // and an event gets fired if a new parameter was added.
    // See the code above where ParameterSync gets instantiated.
    paramSync.addLayer(covLayer)

    if (covLayer.time) {
      new TimeAxis(covLayer).addTo(map)
    }

    layersOnMap.add(covLayer)
    if (!nozoom) {
      map.fitBounds(L.latLngBounds([...layersOnMap.values()].map(l => l.getBounds())),
          { maxZoom: 5 })
    }
  }).on('remove', e => {
    let covLayer = e.target
    layersOnMap.delete(covLayer)
  }).on('dataLoading', () => map.fire('dataloading'))
    .on('dataLoad', () => map.fire('dataload'))

  // TODO use full URI
  if (cov.domainProfiles.some(p => p.endsWith('VerticalProfile'))) {
    // we do that outside of the above 'add' handler since we want to register only once,
    // not every time the layer is added to the map
    let plot
    layer.on('click', () => {
      plot = new ProfilePlot(cov, opts).addTo(map)
    }).on('remove', () => {
      if (plot) {
        map.removeLayer(plot)
      }
    })
  }

  return layer
}

if (window.location.hash) {
  loadCov(window.location.hash.substr(1))
} else {
  for (let url of covs) {
    loadCov(url)
  }
}

new UrlInput({
  position: 'bottomleft'
}).on('submit', e => {
  loadCov(e.url)
}).addTo(map)

const JSONInput_TEMPLATE = `
<template id="template-json-input">

    <form class="hidden">
      <textarea type="text" name="text"></textarea>

      <button style="margin-right: 30px">Load</button>
      <button name="example-grid">Grid</button>
      <button name="example-grid-categories">Grid (categories)</button>
      <button name="example-profile">Profile</button>
      <button name="example-trajectory">Trajectory</button>
      <button name="example-multipolygon">MultiPolygon</button>
      <button name="example-profile-collection">Collection</button>

    </form>
    <button name="expand" data-collapse="Hide" data-expand="Direct Input">Direct Input</button>

</template>
`
inject(JSONInput_TEMPLATE)

let examples = {
  'grid': 'coverages/grid.covjson',
  'grid-categories': 'coverages/grid-categorical.covjson',
  'trajectory': 'coverages/trajectory.covjson',
  'profile': 'coverages/profile.covjson',
  'profile-collection': 'coverages/profile-collection.covjson',
  'multipolygon': 'coverages/multipolygon.covjson'
}

let jsonInput = new JSONInput({
  id: 'template-json-input',
  position: 'bottomleft'
}).on('submit', e => {
  loadCov(e.obj, 'Direct Input')
}).addTo(map)

$.request('get', examples['grid']).then(covjson => {
  jsonInput.json = covjson
})

// add our custom button click handlers
let el = jsonInput.getContainer()

for (let name in examples){
  // we don't use CovJSON.load() here as we want to retain the original formatting
  $.request('get', examples[name]).then(covjson => {
    let btn = $('button', el).filter(b => b.name === 'example-' + name)
    btn.on('click', () => {
      jsonInput.json = covjson
    })
  })
}

<commit_msg>bugfix with collection handling<commit_after>import L from 'leaflet'
import 'leaflet/dist/leaflet.css!'
import 'leaflet-loading'
import 'leaflet-loading/src/Control.Loading.css!'
import 'leaflet-groupedlayercontrol'
import 'leaflet-groupedlayercontrol/dist/leaflet.groupedlayercontrol.min.css!'
import {$} from 'minified'

import * as CovJSON from 'covjson-reader'
import * as RestAPI from 'coverage-rest-client'
import LayerFactory from 'leaflet-coverage'

import Legend from 'leaflet-coverage/controls/Legend.js'
import TimeAxis from 'leaflet-coverage/controls/TimeAxis.js'
import ProfilePlot from 'leaflet-coverage/popups/VerticalProfilePlot.js'
import ParameterSync from 'leaflet-coverage/renderers/ParameterSync.js'
import * as palettes from 'leaflet-coverage/renderers/palettes.js'
import * as transform from 'leaflet-coverage/util/transform.js'
import {inject} from 'leaflet-coverage/controls/utils.js'

import UrlInput from './control.UrlInput.js'
import JSONInput from './control.JSONInput.js'

import './style.css!'

let map = L.map('map', {
  loadingControl: true,
  // initial center and zoom has to be set before layers can be added
  center: [10, 0],
  zoom: 2
})
window.map = map
let baseLayers = {
  'OSM':
    L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
       attribution: 'Map data © OpenStreetMap'
    })
}
baseLayers['OSM'].addTo(map)

let layerControl = L.control.groupedLayers(baseLayers, [], {collapsed: false}).addTo(map)

let layerFactory = LayerFactory()

// some initial test data to play with
let covs = ['coverages/trajectory.covjson',
            'coverages/grid.covjson',
            'coverages/grid-categorical.covjson',
            'coverages/profile.covjson',
            'coverages/profile-collection.covjson',
            'coverages/multipolygon.covjson'
           ]

// We use ParameterSync here so that multiple coverage layers that display the same
// parameter get synchronized in terms of their palette and extent.
// It also allows us to display a single legend only.
// Layers that don't have a single parameter get ignored automatically.
let paramSync = new ParameterSync({
  syncProperties: {
    palette: (p1, p2) => p1,
    paletteExtent: (e1, e2) => e1 && e2 ? [Math.min(e1[0], e2[0]), Math.max(e1[1], e2[1])] : null
  }
}).on('parameterAdd', e => {
    // The virtual sync layer proxies the synced palette, paletteExtent, and parameter.
    // The sync layer will fire a 'remove' event if all real layers for that parameter were removed.
    let layer = e.syncLayer
    if (layer.palette) {
      Legend(layer, {
        position: 'bottomright'
      }).addTo(map)
    }
  })

let layersOnMap = new Set()

function loadCov (url, group=undefined) {
  if (!group) {
    group = url
  }
  map.fire('dataloading')
  CovJSON.read(url)
    .then(cov => RestAPI.wrap(cov, {loader: CovJSON.read}))
    .then(cov => {

    map.fire('dataload')
    console.log('Coverage loaded: ', cov)

    // add each parameter as a layer

    if (cov.coverages) {
      // collection
      if (!cov.parameters) {
        throw new Error('Only coverage collections with a "parameters" property are supported')
      }
      for (let key of cov.parameters.keys()) {
        let opts = {keys: [key]}

        let layers = cov.coverages.map(coverage => createLayer(coverage, opts, true))
        let layer = L.layerGroup(layers)
        layerControl.addOverlay(layer, key, group)
      }
    } else {
      // single coverage

      // TODO use jsonld to properly query graph (together with using cov.id as reference point)
      if (cov.ld.inCollection) {
        group += '
(part of collection)'
      }
      for (let key of cov.parameters.keys()) {
        let opts = {keys: [key]}
        let layer = createLayer(cov, opts)

        layerControl.addOverlay(layer, key, group)
      }
    }
  }).catch(e => {
    map.fire('dataload')
    console.log(e)
    window.alert(e)
  })
}

function createLayer(cov, opts, nozoom) {
  let layer = layerFactory(cov, opts).on('add', e => {
    let covLayer = e.target
    console.log('layer added:', covLayer)

    // This registers the layer with the sync manager.
    // By doing that, the palette and extent get unified (if existing)
    // and an event gets fired if a new parameter was added.
    // See the code above where ParameterSync gets instantiated.
    paramSync.addLayer(covLayer)

    if (covLayer.time) {
      new TimeAxis(covLayer).addTo(map)
    }

    layersOnMap.add(covLayer)
    if (!nozoom) {
      map.fitBounds(L.latLngBounds([...layersOnMap.values()].map(l => l.getBounds())),
          { maxZoom: 5 })
    }
  }).on('remove', e => {
    let covLayer = e.target
    layersOnMap.delete(covLayer)
  }).on('dataLoading', () => map.fire('dataloading'))
    .on('dataLoad', () => map.fire('dataload'))

  // TODO use full URI
  if (cov.domainProfiles.some(p => p.endsWith('VerticalProfile'))) {
    // we do that outside of the above 'add' handler since we want to register only once,
    // not every time the layer is added to the map
    let plot
    layer.on('click', () => {
      plot = new ProfilePlot(cov, opts).addTo(map)
    }).on('remove', () => {
      if (plot) {
        map.removeLayer(plot)
      }
    })
  }

  return layer
}

if (window.location.hash) {
  loadCov(window.location.hash.substr(1))
} else {
  for (let url of covs) {
    loadCov(url)
  }
}

new UrlInput({
  position: 'bottomleft'
}).on('submit', e => {
  loadCov(e.url)
}).addTo(map)

const JSONInput_TEMPLATE = `
<template id="template-json-input">

    <form class="hidden">
      <textarea type="text" name="text"></textarea>

      <button style="margin-right: 30px">Load</button>
      <button name="example-grid">Grid</button>
      <button name="example-grid-categories">Grid (categories)</button>
      <button name="example-profile">Profile</button>
      <button name="example-trajectory">Trajectory</button>
      <button name="example-multipolygon">MultiPolygon</button>
      <button name="example-profile-collection">Collection</button>

    </form>
    <button name="expand" data-collapse="Hide" data-expand="Direct Input">Direct Input</button>

</template>
`
inject(JSONInput_TEMPLATE)

let examples = {
  'grid': 'coverages/grid.covjson',
  'grid-categories': 'coverages/grid-categorical.covjson',
  'trajectory': 'coverages/trajectory.covjson',
  'profile': 'coverages/profile.covjson',
  'profile-collection': 'coverages/profile-collection.covjson',
  'multipolygon': 'coverages/multipolygon.covjson'
}

let jsonInput = new JSONInput({
  id: 'template-json-input',
  position: 'bottomleft'
}).on('submit', e => {
  loadCov(e.obj, 'Direct Input')
}).addTo(map)

$.request('get', examples['grid']).then(covjson => {
  jsonInput.json = covjson
})

// add our custom button click handlers
let el = jsonInput.getContainer()

for (let name in examples){
  // we don't use CovJSON.load() here as we want to retain the original formatting
  $.request('get', examples[name]).then(covjson => {
    let btn = $('button', el).filter(b => b.name === 'example-' + name)
    btn.on('click', () => {
      jsonInput.json = covjson
    })
  })
}

<|endoftext|>


<commit_before>import * as React from 'react'
import * as Reflux from 'reflux'
import * as _ from 'lodash'
import {State} from 'react-router'
import GameNavbar from '../components/game-nav'
import RenderedMap from './map'
import gameActions from '../actions/gameActions'
import ContentEditable from '../components/content-editable'

let Game = React.createClass({
	mixins: [State],
	componentWillReceiveProps(nextProps) {
		let gameId = this.getParams().gameId
		let game = _.find(nextProps.games, {id: gameId})
		if (game) {
			let mapId = game.get('mapId')
			if (mapId) {
				let map = _.find(nextProps.maps, (map) => map.id === mapId.id)
				this.setState({game, map, loading: false, error: ''})
			}
			else {
				this.setState({error: `couldn't find map ${mapId}`})
			}
		}
		else {
			this.setState({error: `couldn't find game ${gameId}`})
		}
	},
	componentWillMount() {
		this.componentWillReceiveProps(this.props)
	},
	getInitialState() {
		return {
			game: null,
			map: null,
			error: null,
			loading: true,
		}
	},
	saveGame() {
		console.log('saving', this.state.game.id)
		this.state.game.save()
			.then((result) => console.log('game saved', result))
			.then(gameActions.updateGameList)
	},
	updateGameTitle(ev) {
		// console.log(ev.target.textContent)
		this.state.game.set('title', ev.target.textContent)
	},
	render() {
		let gameTitle;
		if (this.state.loading)
			gameTitle = 'Loading...'
		if (this.state.game)
			gameTitle = this.state.game.get('title')

		let gameTitleComponent = React.createElement(ContentEditable, {
			className: 'game-title',
			text: gameTitle,
			onInput: this.updateGameTitle,
			onEnter: this.saveGame,
		})

		let title = React.createElement('h1', {className: 'view-title'}, 'Active Game: ', gameTitleComponent)

		let gameNavbar;
		if (this.state.game)
			gameNavbar = React.createElement(GameNavbar, {params: this.getParams(), status: this.state.game.get('status')});

		let map;
		if (this.state.map)
			map = React.createElement(RenderedMap, {map: this.state.map})

		let errorView = React.createElement('div',
			{className: 'error'},
			this.state.error)

		return React.createElement('div',
			{id: 'game'},
			title,
			errorView,
			map,
			gameNavbar)
	},
})

export default Game
<commit_msg>Beginning modifications of game.js<commit_after>import * as React from 'react'
import * as Reflux from 'reflux'
import * as _ from 'lodash'
import {State} from 'react-router'
import GameNavbar from '../components/game-nav'
import RenderedMap from './map'
import gameActions from '../actions/gameActions'
import ContentEditable from '../components/content-editable'

let Game = React.createClass({
	mixins: [State],
	componentWillReceiveProps(nextProps) {
		let gameId = this.getParams().gameId
		let game = _.find(nextProps.games, {id: gameId})
		if (game) {
			let mapId = game.get('mapId')
			if (mapId) {
				let map = _.find(nextProps.maps, (map) => map.id === mapId.id)
				this.setState({game, map, loading: false, error: ''})
			}
			else {
				this.setState({error: `couldn't find map ${mapId}`})
			}
		}
		else {
			this.setState({error: `couldn't find game ${gameId}`})
		}
	},
	componentWillMount() {
		this.componentWillReceiveProps(this.props)
	},
	getInitialState() {
		return {
			game: null,
			map: null,
			error: null,
			loading: true,
		}
	},
	saveGame() {
		console.log('saving', this.state.game.id)
		this.state.game.save()
			.then((result) => console.log('game saved', result))
			.then(gameActions.updateGameList)
	},
	updateGameTitle(ev) {
		// console.log(ev.target.textContent)
		this.state.game.set('title', ev.target.textContent)
	},
	render() {
		if (this.state.loading)
			return React.createElement('h1', {className: 'view-title'}, 'Loading Game...')

		let gameTitle
		if (this.state.game)
			gameTitle = this.state.game.get('title')

		let gameTitleComponent = React.createElement(ContentEditable, {
			className: 'game-title',
			text: gameTitle,
			onInput: this.updateGameTitle,
			onEnter: this.saveGame,
		})

		let title = React.createElement('h1', {className: 'view-title'}, 'Active Game: ', gameTitleComponent)

		let gameNavbar;
		if (this.state.game)
			gameNavbar = React.createElement(GameNavbar, {params: this.getParams(), status: this.state.game.get('status')});

		let map;
		if (this.state.map)
			map = React.createElement(RenderedMap, {map: this.state.map})

		let errorView = React.createElement('div',
			{className: 'error'},
			this.state.error)

		return React.createElement('div',
			{id: 'game'},
			title,
			errorView,
			map,
			gameNavbar)
	},
})

export default Game
<|endoftext|>


<commit_before>/**
 * @license
 * Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
 * This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
 * The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
 * The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
 * Code distributed by Google as part of the polymer project is also
 * subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
 */

(function() {
  'use strict';
  // global for (1) existence means `WebComponentsReady` will file,
  // (2) WebComponents.ready == true means event has fired.
  window.WebComponents = window.WebComponents || {};
  var name = 'webcomponents-loader.js';
  // Feature detect which polyfill needs to be imported.
  var polyfills = [];
  if (!('import' in document.createElement('link'))) {
    polyfills.push('hi');
  }
  if (!('attachShadow' in Element.prototype && 'getRootNode' in Element.prototype) ||
    (window.ShadyDOM && window.ShadyDOM.force)) {
    polyfills.push('sd');
  }
  if (!window.customElements || window.customElements.forcePolyfill) {
    polyfills.push('ce');
  }
  // NOTE: any browser that does not have template or ES6 features
  // must load the full suite (called `lite` for legacy reasons) of polyfills.
  if (!('content' in document.createElement('template')) || !window.Promise || !Array.from ||
    // Edge has broken fragment cloning which means you cannot clone template.content
    !(document.createDocumentFragment().cloneNode() instanceof DocumentFragment)) {
    polyfills = ['lite'];
  }

  if (polyfills.length) {
    var script = document.querySelector('script[src*="' + name +'"]');
    var newScript = document.createElement('script');
    // Load it from the right place.
    var replacement = 'webcomponents-' + polyfills.join('-') + '.js';
    var url = script.src.replace(name, replacement);
    newScript.src = url;
    // NOTE: this is required to ensure the polyfills are loaded before
    // *native* html imports load on older Chrome versions. This *is* CSP
    // compliant since CSP rules must have allowed this script to run.
    // In all other cases, this can be async.
    if (document.readyState === 'loading' && ('import' in document.createElement('link'))) {
      document.write(newScript.outerHTML);
    } else {
      document.head.appendChild(newScript);
    }
  } else {
    // Ensure `WebComponentsReady` is fired also when there are no polyfills loaded.
    // however, we have to wait for the document to be in 'interactive' state,
    // otherwise a rAF may fire before scripts in <body>

    var fire = function() {
      requestAnimationFrame(function() {
        window.WebComponents.ready = true;
        document.dispatchEvent(new CustomEvent('WebComponentsReady', {bubbles: true}));
      });
    };

    if (document.readyState !== 'loading') {
      fire();
    } else {
      document.addEventListener('readystatechange', function wait() {
        fire();
        document.removeEventListener('readystatechange', wait);
      });
    }
  }
})();
<commit_msg>trivial typo in comment<commit_after>/**
 * @license
 * Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
 * This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
 * The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
 * The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
 * Code distributed by Google as part of the polymer project is also
 * subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
 */

(function() {
  'use strict';
  // global for (1) existence means `WebComponentsReady` will fire,
  // (2) WebComponents.ready == true means event has fired.
  window.WebComponents = window.WebComponents || {};
  var name = 'webcomponents-loader.js';
  // Feature detect which polyfill needs to be imported.
  var polyfills = [];
  if (!('import' in document.createElement('link'))) {
    polyfills.push('hi');
  }
  if (!('attachShadow' in Element.prototype && 'getRootNode' in Element.prototype) ||
    (window.ShadyDOM && window.ShadyDOM.force)) {
    polyfills.push('sd');
  }
  if (!window.customElements || window.customElements.forcePolyfill) {
    polyfills.push('ce');
  }
  // NOTE: any browser that does not have template or ES6 features
  // must load the full suite (called `lite` for legacy reasons) of polyfills.
  if (!('content' in document.createElement('template')) || !window.Promise || !Array.from ||
    // Edge has broken fragment cloning which means you cannot clone template.content
    !(document.createDocumentFragment().cloneNode() instanceof DocumentFragment)) {
    polyfills = ['lite'];
  }

  if (polyfills.length) {
    var script = document.querySelector('script[src*="' + name +'"]');
    var newScript = document.createElement('script');
    // Load it from the right place.
    var replacement = 'webcomponents-' + polyfills.join('-') + '.js';
    var url = script.src.replace(name, replacement);
    newScript.src = url;
    // NOTE: this is required to ensure the polyfills are loaded before
    // *native* html imports load on older Chrome versions. This *is* CSP
    // compliant since CSP rules must have allowed this script to run.
    // In all other cases, this can be async.
    if (document.readyState === 'loading' && ('import' in document.createElement('link'))) {
      document.write(newScript.outerHTML);
    } else {
      document.head.appendChild(newScript);
    }
  } else {
    // Ensure `WebComponentsReady` is fired also when there are no polyfills loaded.
    // however, we have to wait for the document to be in 'interactive' state,
    // otherwise a rAF may fire before scripts in <body>

    var fire = function() {
      requestAnimationFrame(function() {
        window.WebComponents.ready = true;
        document.dispatchEvent(new CustomEvent('WebComponentsReady', {bubbles: true}));
      });
    };

    if (document.readyState !== 'loading') {
      fire();
    } else {
      document.addEventListener('readystatechange', function wait() {
        fire();
        document.removeEventListener('readystatechange', wait);
      });
    }
  }
})();
<|endoftext|>


<commit_before>/**
 * Copyright - A Produle Systems Private Limited. All Rights Reserved.
 *
 * @desc Entry point act as a starting point for application
 *
 */

function UC_MainController()
{
	var thisClass = this;

	this.appController = new UC_AppController();
	this.dashboardController = new UC_DashboardController();
	this.visitorListController = new UC_VisitorListController();
	this.userController = new UC_UserController();
	this.filterController = new UC_FilterController();
	this.messagingController = new UC_MessagingController();
	this.rtcController = new UC_RTCController();

    this.rivetUserNameObj = null;

	this.constructor = function()
	{

        thisClass.appController.constructor();
        thisClass.dashboardController.constructor();
        thisClass.visitorListController.constructor();
        thisClass.userController.constructor();
        thisClass.filterController.constructor();
        thisClass.messagingController.constructor();

		thisClass.rtcController.sessionID = UC_Utils.guidGenerator();
		thisClass.rtcController.connect();

        thisClass.initRivetBinds();

        $(".uc_tab_trigger").on("click",thisClass.toggleTabs);

        $(document).on("click",".ucSwitchContentTrigger",thisClass.switchContent);

        $(".ucSwitchContentContainer").hide();
        $("#uc_tab_data_dashboard").show();
        $("#ucMainDashboard").addClass("ucCurrentPage");

        $(".ucSideBarMenuTrigger").on("click",thisClass.menuContainer);
        $(".ucSidebarCloseBtn").on("click",thisClass.closeMenuContainer);

        $(".mfTooltip").tooltip();

        $(document).on('click', function (e) {
            $('[data-toggle="popover"],[data-original-title]').each(function () {
                if (!$(this).is(e.target) && $(this).has(e.target).length === 0 && $('.popover').has(e.target).length === 0) {
                    (($(this).popover('hide').data('bs.popover')||{}).inState||{}).click = false  // fix for BS 3.3.6
                }

            });
        });

        $(window).resize(function(){
        	thisClass.interfaceResize();
        });

        thisClass.interfaceResize();
	};

    /*
     * @desc Init all Binding reference objects for rivets
     */
	this.initRivetBinds = function()
	{
		thisClass.rivetUserNameObj = rivets.bind(
            document.querySelector('#uc_currentuser_name'), {
                currentUserName: UC_UserSession.user.firstName+" "+UC_UserSession.user.lastName
            }
        );
	};

    /**
     * @desc Toggles between tabs. This is a global function, not specific to specific tab group
     */
    this.toggleTabs = function()
    {
        $("."+$(this).attr("data-tabgroup-class")).hide();
        $("#"+$(this).attr("data-tabgroup-tabid")).show();

        $(this).closest(".btn-group").find(".btn").removeClass("active");
        $(this).addClass("active");
    };

    /**
     * @desc
     */
    this.switchContent = function(e)
    {
    	var divID = $(this).attr("switch-to");
        $(".ucSwitchContentContainer").hide();
        $("#"+divID).show();

        $(".ucSwitchContentTrigger").removeClass("ucCurrentPage");
        $(this).addClass("ucCurrentPage");

        thisClass.closeMenuContainer(e);
    };
    /**
     * @desc Responsive for Sidebar
     */

    this.menuContainer = function(e)
    {
    		e.stopPropagation();

    	    if($('.ucSidebar,.ucMainContent,.ucIndexPageWrapper, body,  #ucHeader').hasClass('open'))
    	    {
    	    	$( ".ucSidebar,.ucMainContent,.ucIndexPageWrapper, body , #ucHeader" ).removeClass( "open" );
    	    	$("html, body").css({"overflow": "auto"});

    	    } else {
    	    	$( ".ucSidebar,.ucMainContent, .ucIndexPageWrapper, body , #ucHeader" ).addClass( "open" );
    	    	$("html, body").css({"overflow": "hidden"});

    	    }

    };
    /**
     * @desc Closing the sidebar
     */

    this.closeMenuContainer = function(e)
    {
    		e.stopPropagation();
    		$( ".ucSidebar,.ucMainContent,.ucIndexPageWrapper, body , #ucHeader" ).removeClass( "open" );
    		$("html, body").css({"overflow": "auto"});

    };
    /**
     * @desc Resize Div layouts by calculating current window size so we have a fixed window size application like a desktop app
     */
    this.interfaceResize  = function()
	{

    	 $('.ucSidebar').height($(window).height()-$('.ucSidebar').offset().top-30);

    	 var sidebarWidth = 0;
    	 if($('.ucSidebar').css("display") == "block")
    	{
    		 sidebarWidth = $('.ucSidebar').width() + 40;

    	}

    	 $('.ucTableWrapper').height($(window).height()-178);
    	 $('.ucTableWrapper').width($(window).width()-sidebarWidth);
	};
}
<commit_msg>UC-88 Responsive code updated<commit_after>/**
 * Copyright - A Produle Systems Private Limited. All Rights Reserved.
 *
 * @desc Entry point act as a starting point for application
 *
 */

function UC_MainController()
{
	var thisClass = this;

	this.appController = new UC_AppController();
	this.dashboardController = new UC_DashboardController();
	this.visitorListController = new UC_VisitorListController();
	this.userController = new UC_UserController();
	this.filterController = new UC_FilterController();
	this.messagingController = new UC_MessagingController();
	this.rtcController = new UC_RTCController();

    this.rivetUserNameObj = null;

	this.constructor = function()
	{

        thisClass.appController.constructor();
        thisClass.dashboardController.constructor();
        thisClass.visitorListController.constructor();
        thisClass.userController.constructor();
        thisClass.filterController.constructor();
        thisClass.messagingController.constructor();

		thisClass.rtcController.sessionID = UC_Utils.guidGenerator();
		thisClass.rtcController.connect();

        thisClass.initRivetBinds();

        $(".uc_tab_trigger").on("click",thisClass.toggleTabs);

        $(document).on("click",".ucSwitchContentTrigger",thisClass.switchContent);

        $(".ucSwitchContentContainer").hide();
        $("#uc_tab_data_dashboard").show();
        $("#ucMainDashboard").addClass("ucCurrentPage");

        $(".ucSideBarMenuTrigger").on("click",thisClass.menuContainer);
        $(".ucSidebarCloseBtn").on("click",thisClass.closeMenuContainer);

        $(".mfTooltip").tooltip();

        $(document).on('click', function (e) {
            $('[data-toggle="popover"],[data-original-title]').each(function () {
                if (!$(this).is(e.target) && $(this).has(e.target).length === 0 && $('.popover').has(e.target).length === 0) {
                    (($(this).popover('hide').data('bs.popover')||{}).inState||{}).click = false  // fix for BS 3.3.6
                }

            });
        });

        $(window).resize(function(){
        	thisClass.interfaceResize();
        });

        thisClass.interfaceResize();
	};

    /*
     * @desc Init all Binding reference objects for rivets
     */
	this.initRivetBinds = function()
	{
		thisClass.rivetUserNameObj = rivets.bind(
            document.querySelector('#uc_currentuser_name'), {
                currentUserName: UC_UserSession.user.firstName+" "+UC_UserSession.user.lastName
            }
        );
	};

    /**
     * @desc Toggles between tabs. This is a global function, not specific to specific tab group
     */
    this.toggleTabs = function()
    {
        $("."+$(this).attr("data-tabgroup-class")).hide();
        $("#"+$(this).attr("data-tabgroup-tabid")).show();

        $(this).closest(".btn-group").find(".btn").removeClass("active");
        $(this).addClass("active");
    };

    /**
     * @desc
     */
    this.switchContent = function(e)
    {
    	var divID = $(this).attr("switch-to");
        $(".ucSwitchContentContainer").hide();
        $("#"+divID).show();

        $(".ucSwitchContentTrigger").removeClass("ucCurrentPage");
        $(this).addClass("ucCurrentPage");

        thisClass.closeMenuContainer(e);
    };
    /**
     * @desc Responsive for Sidebar
     */

    this.menuContainer = function(e)
    {
    		e.stopPropagation();

    	    if($('.ucSidebar,.ucMainContent,.ucIndexPageWrapper, body,  #ucHeader').hasClass('open'))
    	    {
    	    	$( ".ucSidebar,.ucMainContent,.ucIndexPageWrapper, body , #ucHeader" ).removeClass( "open" );
    	    	$("html, body").css({"overflow": "auto"});

    	    } else {
    	    	$( ".ucSidebar,.ucMainContent, .ucIndexPageWrapper, body , #ucHeader" ).addClass( "open" );
    	    	$("html, body").css({"overflow": "hidden"});

    	    }

    };
    /**
     * @desc Closing the sidebar
     */

    this.closeMenuContainer = function(e)
    {
    		e.stopPropagation();
    		$( ".ucSidebar,.ucMainContent,.ucIndexPageWrapper, body , #ucHeader" ).removeClass( "open" );
    		$("html, body").css({"overflow": "auto"});

    };
    /**
     * @desc Resize Div layouts by calculating current window size so we have a fixed window size application like a desktop app
     */
    this.interfaceResize  = function()
	{
    	if($('.ucSidebar').length==1)
    		{
    		$('.ucSidebar').height($(window).height()-$('.ucSidebar').offset().top-30);
    		}

    	 var sidebarWidth = 0;
    	 if($('.ucSidebar').css("display") == "block")
    	{
    		 sidebarWidth = $('.ucSidebar').width() + 40;

    	}

    	 $('.ucTableWrapper').height($(window).height()-195);
    	 $('.ucTableWrapper').width($(window).width()-sidebarWidth-20);
	};
}
<|endoftext|>