<repo_name>BoghaFisch/lab4<file_sep>/js/model/dinnerModel.js
//DinnerModel Object constructor
var DinnerModel = function() {

	//TODO Lab 2 implement the data structure that will hold number of guest
	// and selected dinner options for dinner menu

  // The api key
  var apiKey = "<KEY>";

  // Dish to be shown in dish details
  var detailedDish = null;
  var detailedDishType = null;

  // menu[0] == starter, menu[1] == main course, menu[2] == dessert
  var menu = [null, null, null];

  // The number of guests
  var numberOfGuests = 4;

  // Array of listeners
  var listeners = [];

  this.addObserver = function(observer) {
    listeners.push(observer);
  }
  var notifyObservers = function(component, obj) {
    if (obj) {
      for (var i = 0; i < listeners.length; i++) {
        listeners[i].update(component, obj);
      }
    }
    else {
      for (var i = 0; i < listeners.length; i++) {
        listeners[i].update(component);
      }
    }
  }

  this.getDetailedDish = function() {
    return detailedDish;
  }
  this.getDetailedDishType = function() {
    return detailedDishType;
  }
  this.setDetailedDish = function(id, type) {

    if (id == null) {
        detailedDish = null;
        detailedDishType = null;
        notifyObservers("detailedDish");
    }
    else {
      var recipeId = id;
      var url = "http://api.bigoven.com/recipe/" + recipeId + "?api_key=" + apiKey;
      $.ajax({
          type: "GET",
          dataType: 'json',
          cache: false,
          url: url,
          success: function (data) {
              detailedDish = data;
              detailedDishType = type;
              notifyObservers("detailedDish");
          },
          error: function() {
            notifyObservers("error");
          }
      });
    }
  }
	this.setNumberOfGuests = function(num) {
		numberOfGuests = num;
    notifyObservers("guests");
	}

	// Return number of guests
	this.getNumberOfGuests = function() {
    return numberOfGuests;
	}

	//Returns the dish that is on the menu for selected type
	this.getSelectedDish = function(type) {
		return menu[type];
	}
	//Returns all the dishes on the menu.
	this.getFullMenu = function() {
    var rMenu = [];
    for (var i = 0; i < menu.length; i++) {
      rMenu.push(menu[i]);
    }
		return rMenu;
	}
  this.menuFull = function() {
    for (var i = 0; i < menu.length; i++) {
      if (menu[i] == null) {
        return false;
      }
    }
    return true;
  }
	//Returns all ingredients for all the dishes on the menu.
	this.getAllIngredients = function() {
    var rIngredients = [];
    for (var i = 0; i < menu.length; i++) {
      if (menu[i] != null) {
        for (var j = 0; j < menu[i].ingredients.length; j++) {
          rIngredients.push(menu[i].ingredients[j]);
        }
      }
    }
	}

	//Returns the total price of the menu (all the ingredients multiplied by number of guests).
	this.getTotalMenuPrice = function() {
		var totPrice = 0;
    for (var i = 0; i < menu.length; i++) {
      if (menu[i] != null) {
        totPrice += this.getDishPrice(menu[i]);
      }
    }
    return totPrice;
	}

	//Adds the passed dish to the menu. If the dish of that type already exists on the menu
	//it is removed from the menu and the new one added.
	this.addDishToMenu = function(id, type) {
    var recipeId = id;
    var url = "http://api.bigoven.com/recipe/" + recipeId + "?api_key=" + apiKey;
    $.ajax({
        type: "GET",
        dataType: 'json',
        cache: false,
        url: url,
        success: function (data) {
            if (type == "starter") {
              menu[0] = data;
            }
            else if (type == "main dish"){
              menu[1] = data;
            }
            else if (type == "dessert") {
              menu[2] = data;
            }
            notifyObservers("menu");
        },
        error: function() {
          notifyObservers("error");
        }
    });
	}

	//Removes dish from menu
	this.removeDishFromMenu = function(id) {
		for (var i = 0; i < menu.length; i++) {
      if (menu[i] != null) {
        if (menu[i].RecipeID == id) {
          menu[i] = null;
        }
      }
    }
    notifyObservers("menu");
	}


  this.getAllDishes = function (type,filter) {
    var titleKeyword = type;
    if (filter) {
      var url = "http://api.bigoven.com/recipes?pg=1&rpp=25&title_kw="
            + titleKeyword
            + "&title_kw="+filter
            + "&api_key="+apiKey;
    }
    else {
      var url = "http://api.bigoven.com/recipes?pg=1&rpp=25&title_kw="
            + titleKeyword
            + "&api_key="+apiKey;
    }

    $.ajax({
          type: "GET",
          dataType: 'json',
          cache: false,
          url: url,
          success: function (data) {
            notifyObservers("dishes", data.Results);
          },
          error: function() {
            notifyObservers("error");
          }
      });
  }

	//function that returns a dish of specific ID
	this.getDish = function (id) {
    var recipeId = id;
    var url = "http://api.bigoven.com/recipe/" + recipeId + "?api_key=" + apiKey;
    $.ajax({
        type: "GET",
        dataType: 'json',
        cache: false,
        url: url,
        success: function (data) {
            notifyObservers("getDish", data);
        },
        error: function() {
          notifyObservers("error");
        }
    });
	}
  // Returns the total price for the dish
  this.getDishPrice = function(dish) {
    var price = 0;
    for (var i = 0; i < dish.Ingredients.length; i++) {
      price += dish.Ingredients[i].MetricQuantity;
    }
    price = price * numberOfGuests;
    return price;
  }
}
<file_sep>/js/view/dinnerInstructionsView.js
var DinnerInstructionsView = function (container, model) {

  this.populateTable = function() {
    this.container.empty();

    var menu = model.getFullMenu();

    // Append top navigation banner
    this.container.append("<div class='row myDinnerBanner'><div class='col-md-6'><h3>My Dinner: "+model.getNumberOfGuests()
      +" people</h3></div><div class='col-md-6'><button type='submit' class='btn btn-default pull-right backToEditButton' id='iBackToEdit'>Go back and edit dinner</button></div></div>");

    // Create preparations-list
    var ditString = "<table id='dinnerInstructionsTable'>";
    for (var i = 0; i < menu.length; i++) {
      if (menu[i] != null) {
        ditString += "<tr>";

        // Add image column
        ditString += "<td class='col-md-2 imageCol'><img class='instructionsImage' src='"+menu[i].ImageURL+"'></td>";

        // Add Dinner Description
        ditString += "<td class='col-md-4 desciptionCol'><h3>"+menu[i].Title+"</h3>"+menu[i].Description+"</td>";

        // Add preparations
        ditString += "<td class='col-md-6 preparationCol'><h4>Preparation</h4>"+menu[i].Instructions+"</td></tr>";
      }
    }

    // Close table
    ditString += "</table>";

    // Append html to container
    this.container.append(ditString);
  }
  this.setLoading = function() {
    container.empty();
  }

  // Shows view
  this.show = function() {

    // Show container
    container.show();
  }
  // Hides view
  this.hide = function() {
    container.hide();
  }
  this.update = function(component, obj) {
    if (component == "menu" || "guests")
      this.populateTable();
  }
  this.container = container;
  this.populateTable();
  this.backToEditButton = $("#iBackToEdit");
  model.addObserver(this);
}
<file_sep>/js/controller/dishDetailsController.js
var DishDetailsController = function(app, view, model ) {
  view.container.on("click", "#backToSelectButton", function() {
    model.setDetailedDish(null);
    app.showView("selectDish");
  });
  view.container.on("click", "#confirmDishButton", function() {
    model.addDishToMenu(model.getDetailedDish().RecipeID, model.getDetailedDishType());
    model.setDetailedDish(null);
    app.showView("selectDish", true);
  });
}
<file_sep>/js/view/selectDishView.js
var SelectDishView = function (container, model) {

  this.populateTable = function(dishes) {

    this.dishesResultsTable.empty();
    var resultsString = "<tr id='firstRow'>";
    for (var i = 0; i < dishes.length; i++) {
      // For every 4th dish, make a new row in the beginning
      if (i % 5 == 0) {
        resultsString += "</tr><tr>";
      }
      // Append the image, name and description to table
      resultsString += "<td><figure class='dishResultsFigure'><a href='#'><img class='dishResultsImage' id='dish"+dishes[i].RecipeID+"' src='"+dishes[i].ImageURL+"'></a><figcaption style='text-align:center;'>"+dishes[i].Title+"</figcaption></figure>";
      resultsString += "<div class='dishDescription'>"+dishes[i].Subcategory+"</div></td>";

    }
    // Add remaining td's for sizing
    while (i % 5 != 0) {
      resultsString += "<td></td>";
      i++;
    }
    resultsString +="</tr>";
    this.dishesResultsTable.append(resultsString);
  }
  this.setLoading = function() {
    this.dishesResultsTable.empty();
    this.dishesResultsTable.append("<h3>Searching..</h3>");
  }
  this.setError = function() {
    this.dishesResultsTable.empty();
    this.dishesResultsTable.append("<h3>Error.. Please check your internet connection.</h3>");
  }
  this.show = function() {
    // Show container
    container.show();

  }
  this.hide = function() {
    container.hide();
  }
  this.update = function(component, obj) {
    if (component == "error") {
      this.setError();
    }
    else if (component == "dishes") {
      this.populateTable(obj);
    }
  }
  this.container = container;
  this.dishesResultsTable = $("#dishesResultsTable");

  // Add itself as listener
  model.addObserver(this);

  // Finally, populate the dishes table
  model.getAllDishes("starter");
}
<file_sep>/README.md
# iprog16_lab4
A web-application for planning dinners.

The application is built using MVC with Javascript/jQuery and HTML/CSS. It uses jQuery ajax-calls to fetch recipes from the REST API BigOven.  
<file_sep>/js/controller/dinnerInstructionsViewController.js
var DinnerInstructionsViewController = function(app, view, model ) {
  view.container.on("click", "#iBackToEdit", function() {
    app.showView("selectDish");
  });
}
<file_sep>/js/controller/homeViewController.js
var HomeViewController = function(app, view, model ) {

  // Switch to selectDishView
  view.newDinnerButton.click(function() {
    app.showView("selectDish");
  });
}
<file_sep>/js/controller/dinnerOverviewViewController.js
var DinnerOverviewViewController = function(app, view, model ) {
  view.numberOfGuestsInput.change(function() {
    model.setNumberOfGuests(view.numberOfGuestsInput.val());
  });
  view.confirmButton.click(function(){
    model.setDetailedDish(null);
    app.showView("dinnerSummary");
  });
  view.container.on("click", "#rmStarter", function() {
    model.removeDishFromMenu(model.getFullMenu()[0].RecipeID);
  });
  view.container.on("click", "#rmMain", function() {
    model.removeDishFromMenu(model.getFullMenu()[1].RecipeID);
  });
  view.container.on("click", "#rmDessert", function() {
    model.removeDishFromMenu(model.getFullMenu()[2].RecipeID);
  });
}
<file_sep>/js/view/dishDetails.js
var DishDetailsView = function (container, model) {
  this.populate = function() {
    if (model.getDetailedDish() != null) {
      // Empty containers
      this.dishDescription.empty();
      this.dishIngredients.empty();
      this.preparationInstructions.empty();

      // Get the selected dish to show details for
      var selectedDish = model.getDetailedDish();
      var dishPrice = model.getDishPrice(selectedDish);

      // Add dish name, description and image to container
      this.dishDescription.append("<h3 id='dishDetailsHeader'>"+selectedDish.Title+"</h3>");
      this.dishDescription.append("<figure id='selectedDishFig'><img src='"+selectedDish.ImageURL+"' ><figcaption>"+selectedDish.Description+"</figcaption></figure>");
      this.dishDescription.append("<button class='btn btn-default' id='backToSelectButton' type='submit'>Back to Select Dish</button>");

      // Add the list of ingredients table header
      var numPeople = model.getNumberOfGuests();
      this.dishIngredients.append("<h4 id='ingredientsHeader'>INGREDIENTS FOR "+numPeople+" PEOPLE</h4>");
      this.dishIngredients.append("<hr class='breakLine'>");

      // Add table
      var ingredients = selectedDish.Ingredients;
      var tString = "<table id='dtIngredientsTable'>";
      for (var i = 0; i < ingredients.length; i++) {
        tString += "<tr><td class='ingredientAmount'>"+(ingredients[i].MetricQuantity * numPeople) + ingredients[i].MetricUnit + "</td><td class='ingredientName'>"
          + ingredients[i].Name + "</td><td class='ingredientPrice'>SEK"+ (ingredients[i].MetricQuantity * numPeople) + "</td></tr>";
      }
      tString += "</table>";
      this.dishIngredients.append(tString);
      this.dishIngredients.append("<hr class='breakLine'>");
      this.dishIngredients.append("<div class='row'>");
      this.dishIngredients.append("<div class='col-md-6'><button class='btn btn-default' id='confirmDishButton' type='submit'>Confirm Dish</button></div>");

      this.dishIngredients.append("<div class='col-md-6'>SEK "+dishPrice+"</div>");

      // Add preparation instructions
      this.preparationInstructions.append("<h3>Preparation</h3>");
      this.preparationInstructions.append(selectedDish.Instructions);
    }
  }
  this.setLoading = function() {
    // Empty containers
    this.dishDescription.empty();
    this.dishIngredients.empty();
    this.preparationInstructions.empty();

    // Append loading message
    this.dishDescription.append("<h3>Loading dish..</h3>");
    this.dishIngredients.append("<h3>Loading ingredients..</h3>");
  }
  this.setError = function() {
    // Empty containers
    this.dishDescription.empty();
    this.dishIngredients.empty();
    this.preparationInstructions.empty();

    // Append error messages to description and ingredients
    this.dishDescription.append("<h3>Error.. Please check your internet connection.</h3>");
    this.dishIngredients.append("<h3>Error.. Please check your internet connection.</h3>");
  }
  this.show = function() {
    // Show container
    container.show();
  }
  this.hide = function() {
    container.hide();
  }
  this.update = function(component, obj) {
    if (component == "error") {
      this.showError();
    }
    else if (component == "guests" || component == "detailedDish") {
       this.populate();
    }
  }
  // Set container variables
  this.container = container;
  this.dishDescription = $("#dishDescription");
  this.dishIngredients = $("#dishIngredients");
  this.preparationInstructions = $("#preparationInstructions");

  // Populate the container
  this.populate();

  // Add as model listener
  model.addObserver(this);
}
<file_sep>/js/controller/selectDishViewController.js
var SelectDishViewController = function(app, view, model ) {
  view.container.on("click", ".dishResultsImage", function(event) {
    var type = $("#selectType").val();
    var id = event.target.id;
    id = id.replace("dish", "");
    model.setDetailedDish(id, type);
    app.showView("dishDetails", true);
  });
  $('#selectType').on('change', function() {
    var filter = $("#searchBox").val();
    view.setLoading();
    if (filter != "")
      model.getAllDishes(this.value, filter);
    else {
      model.getAllDishes(this.value);
    }
  });
  $('#submitSearch').click(function(event) {
    event.preventDefault();
    var filter = $("#searchBox").val();
    var type = $("#selectType").val();
    view.setLoading();
    model.getAllDishes(type, filter);
  });
}
