text: // Global app controller
  import Search from './models/Search';
  import Recipe from './models/Recipe';
  import List from './models/List';
  import Likes from './models/Likes';
  import { elements, renderLoader, clearLoader } from './views/base';
  import * as searchView from './views/searchView';
  import * as recipeView from './views/recipeView';
  import * as listView from './views/listView';
  import * as likesView from './views/likesView';
  
  /* GLOBAL STATE
   * - Search object
   * - Current recipe object
   * - Shopping list object
   * - Liked recipes
   */
  
  const state = {};
  
  /*
   * SEARCH CONTROLLER
   */
  const controlSearch = async () => {
    // 1. Get query from view
    const query = searchView.getInput();
  
    if (query) {
      // 2. New Search object added to state
      state.search = new Search(query);
  
      // 3. Prepare UI for results
      searchView.clearInput();
      searchView.clearResults();
      renderLoader(elements.searchRes);
  
      try {
        // 4. Searchj for recipes
        await state.search.getResults();
  
        // 5. Display results on UI
        clearLoader();
        searchView.renderResults(state.search.result);
      } catch (error) {
        console.log(`Error: error processing search - ${error}`);
        clearLoader();
      }
    }
  };
  
  elements.searchForm.addEventListener('submit', e => {
    e.preventDefault();
    controlSearch();
  });
  
  elements.searchResPages.addEventListener('click', e => {
    const btn = e.target.closest('.btn-inline');
    if (btn) {
      const goToPage = parseInt(btn.dataset.goto, 10);
      searchView.clearResults();
      searchView.renderResults(state.search.result, goToPage);
    }
  });
  
  /*
   * RECIPE CONTROLLER
   */
  
  const controlRecipe = async () => {
    let id = parseInt(window.location.hash.substr(1), 10);
  
    if (id) {
      recipeView.clearRecipe();
      renderLoader(elements.recipe);
  
      if (state.search) {
        searchView.highlightSelected(id);
      }
  
      state.recipe = new Recipe(id);
  
      try {
        await state.recipe.getRecipe();
  
        state.recipe.calcTime();
        state.recipe.calcServings();
        state.recipe.parseIngredients();
  
        clearLoader();
        recipeView.renderRecipe(state.recipe, state.likes.isLiked(id));
      } catch (error) {
        console.log(`Error: error processing recipe - ${error}`);
      }
    }
  };
  
  ['hashchange', 'load'].forEach(event =>
    window.addEventListener(event, controlRecipe)
  );
  
  /*
   * LIST CONTROLLER
   */
  const controlList = () => {
    if (!state.list) state.list = new List();
    state.recipe.ingredients.forEach(el => {
      const item = state.list.addItem(el.count, el.unit, el.ingredient);
      listView.renderItem(item);
    });
  };
  
  elements.shopping.addEventListener('click', e => {
    const id = e.target.closest('.shopping__item').dataset.itemid;
  
    if (e.target.matches('.shopping__delete, .shopping__delete *')) {
      state.list.deleteItem(id);
      listView.deleteItem(id);
    } else if (e.target.matches('.shopping__count--value')) {
      const val = parseFloat(e.target.value, 10);
      state.list.updateCount(id, val);
    }
  });
  
  /*
   * LIKE CONTROLLER
   */
  
  const controlLike = () => {
    if (!state.likes) {
      state.likes = new Likes();
    }
  
    const currentID = state.recipe.id;
  
    if (!state.likes.isLiked(currentID)) {
      const newLike = state.likes.addLike(
        currentID,
        state.recipe.title,
        state.recipe.author,
        state.recipe.img
      );
      likesView.toggleLikeBtn(true);
      likesView.renderLike(newLike);
    } else {
      state.likes.deleteLike(currentID);
      likesView.toggleLikeBtn(false);
      likesView.deleteLike(currentID);
    }
  
    likesView.toggleLikeMenu(state.likes.getNumLikes());
  };
  
  window.addEventListener('load', e => {
    state.likes = new Likes();
    state.likes.readStorage();
    likesView.toggleLikeMenu(state.likes.getNumLikes());
    state.likes.likes.forEach(like => likesView.renderLike(like));
  });
  
  elements.recipe.addEventListener('click', e => {
    if (e.target.matches('.btn-decrease, .btn-decrease *')) {
      if (state.recipe.servings > 1) {
        state.recipe.updateServings('dec');
        recipeView.updateServingsIngredients(state.recipe);
      }
    } else if (e.target.matches('.btn-increase, .btn-increase *')) {
      state.recipe.updateServings('inc');
      recipeView.updateServingsIngredients(state.recipe);
    } else if (e.target.matches('.recipe-btn--add, .recipe-btn--add *')) {
      controlList();
    } else if (e.target.matches('.recipe__love, .recipe__love *')) {
      controlLike();
    }
  });
  