text: //Reusable Fields
  const getSellFields = function () {
    let fields = []
    for (var i = 2; i < 14; i++) {
      fields.push($("#sell_" + i)[0])
    }
    return fields
  }
  
  const getFirstBuyRadios = function () {
    return [
      $("#first-time-radio-no")[0],
      $("#first-time-radio-yes")[0]
    ];
  }
  
  const getPreviousPatternRadios = function () {
    return [
      $("#pattern-radio-unknown")[0],
      $("#pattern-radio-fluctuating")[0],
      $("#pattern-radio-small-spike")[0],
      $("#pattern-radio-large-spike")[0],
      $("#pattern-radio-decreasing")[0]
    ];
  }
  
  const getCheckedRadio = function (radio_array) {
    return radio_array.find(radio => radio.checked === true).value;
  }
  
  const checkRadioByValue = function (radio_array, value) {
    if (value === null) {
      return;
    }
    value = value.toString();
    radio_array.find(radio => radio.value == value).checked = true;
  }
  
  const sell_inputs = getSellFields()
  const buy_input = $("#buy")
  const first_buy_radios = getFirstBuyRadios()
  const previous_pattern_radios = getPreviousPatternRadios()
  const permalink_input = $('#permalink-input')
  const permalink_button = $('#permalink-btn')
  const snackbar = $('#snackbar')
  
  //Functions
  const fillFields = function (prices, first_buy, previous_pattern) {
    checkRadioByValue(first_buy_radios, first_buy);
    checkRadioByValue(previous_pattern_radios, previous_pattern);
  
    buy_input.focus();
    buy_input.val(prices[0] || '')
    buy_input.blur();
    const sell_prices = prices.slice(2)
  
    sell_prices.forEach((price, index) => {
      if (!price) {
        return
      } else {
        const element = $("#sell_" + (index + 2));
        element.focus();
        element.val(price);
        element.blur();
      }
    })
  }
  
  const initialize = function () {
    try {
      const previous = getPrevious();
      const first_buy = previous[0];
      const previous_pattern = previous[1];
      const prices = previous[2];
      if (prices === null) {
        fillFields([], first_buy, previous_pattern)
      } else {
        fillFields(prices, first_buy, previous_pattern)
      }
    } catch (e) {
      console.error(e);
    }
  
    $(document).trigger("input");
  
    $("#permalink-btn").on("click", copyPermalink)
  
    $("#reset").on("click", function () {
      if (window.confirm(i18next.t("prices.reset-warning"))) {
        sell_inputs.forEach(input => input.value = '')
        fillFields([], false, -1)
        update()
      }
    })
  }
  
  const updateLocalStorage = function (prices, first_buy, previous_pattern) {
    try {
      if (prices.length !== 14) throw "The data array needs exactly 14 elements to be valid"
      localStorage.setItem("sell_prices", JSON.stringify(prices))
      localStorage.setItem("first_buy", JSON.stringify(first_buy));
      localStorage.setItem("previous_pattern", JSON.stringify(previous_pattern));
    } catch (e) {
      console.error(e)
    }
  }
  
  const isEmpty = function (arr) {
    const filtered = arr.filter(value => value !== null && value !== '' && !isNaN(value))
    return filtered.length == 0
  }
  
  const getFirstBuyStateFromQuery = function (param) {
    try {
      const params = new URLSearchParams(window.location.search.substr(1));
      const firstbuy_str = params.get(param);
  
      if (firstbuy_str == null) {
        return null;
      }
  
      firstbuy = null;
      if (firstbuy_str == "1" || firstbuy_str == "yes" || firstbuy_str == "true") {
        firstbuy = true;
      } else if (firstbuy_str == "0" || firstbuy_str == "no" || firstbuy_str == "false") {
        firstbuy = false;
      }
  
      return firstbuy;
  
    } catch (e) {
      return null;
    }
  }
  
  const getFirstBuyStateFromLocalstorage = function () {
    return JSON.parse(localStorage.getItem('first_buy'))
  }
  
  const getPreviousPatternStateFromLocalstorage = function () {
    return JSON.parse(localStorage.getItem('previous_pattern'))
  }
  
  const getPreviousPatternStateFromQuery = function (param) {
    try {
      const params = new URLSearchParams(window.location.search.substr(1));
      const pattern_str = params.get(param);
  
      if (pattern_str == null) {
        return null;
      }
  
      if (pattern_str == "0" || pattern_str == "fluctuating") {
        pattern = 0;
      } else if (pattern_str == "1" || pattern_str == "large-spike") {
        pattern = 1;
      } else if (pattern_str == "2" || pattern_str == "decreasing") {
        pattern = 2;
      } else if (pattern_str == "3" || pattern_str == "small-spike") {
        pattern = 3;
      } else {
        pattern = -1;
      }
  
      return pattern;
  
    } catch (e) {
      return null;
    }
  }
  
  const getPricesFromLocalstorage = function () {
    try {
      const sell_prices = JSON.parse(localStorage.getItem("sell_prices"));
  
      if (!Array.isArray(sell_prices) || sell_prices.length !== 14) {
        return null;
      }
  
      return sell_prices;
    } catch (e) {
      return null;
    }
  };
  
  const getPricesFromQuery = function (param) {
    try {
      const params = new URLSearchParams(window.location.search.substr(1));
      const sell_prices = params.get(param).split(".").map((x) => parseInt(x, 10));
  
      if (!Array.isArray(sell_prices)) {
        return null;
      }
  
      // Parse the array which is formatted like: [price, M-AM, M-PM, T-AM, T-PM, W-AM, W-PM, Th-AM, Th-PM, F-AM, F-PM, S-AM, S-PM, Su-AM, Su-PM]
      // due to the format of local storage we need to double up the price at the start of the array.
      sell_prices.unshift(sell_prices[0]);
  
      // This allows us to fill out the missing fields at the end of the array
      for (let i = sell_prices.length; i < 14; i++) {
        sell_prices.push(0);
      }
  
      return sell_prices;
    } catch (e) {
      return null;
    }
  };
  
  const getPreviousFromQuery = function () {
  
    /* Check if valid prices are entered. Exit immediately if not. */
    prices = getPricesFromQuery("prices");
    if (prices == null) {
      return null;
    }
  
    console.log("Using data from query.");
    window.populated_from_query = true;
    return [
      getFirstBuyStateFromQuery("first"),
      getPreviousPatternStateFromQuery("pattern"),
      prices
    ];
  };
  
  const getPreviousFromLocalstorage = function () {
    return [
      getFirstBuyStateFromLocalstorage(),
      getPreviousPatternStateFromLocalstorage(),
      getPricesFromLocalstorage()
    ];
  };
  
  
  /**
   * Gets previous values. First tries to parse parameters,
   * if none of them match then it looks in local storage.
   * @return {[first time, previous pattern, prices]}
   */
  const getPrevious = function () {
    return getPreviousFromQuery() || getPreviousFromLocalstorage();
  };
  
  const getSellPrices = function () {
    //Checks all sell inputs and returns an array with their values
    return res = sell_inputs.map(function (input) {
      return parseInt(input.value || '');
    })
  }
  
  const calculateOutput = function (data, first_buy, previous_pattern) {
    if (isEmpty(data)) {
      $("#output").html("");
      return;
    }
    let output_possibilities = "";
    let analyzed_possibilities = analyze_possibilities(data, first_buy, previous_pattern);
    previous_pattern_number = ""
    for (let poss of analyzed_possibilities) {
      var out_line = "<tr><td class='table-pattern'>" + poss.pattern_description + "</td>"
      if (previous_pattern_number != poss.pattern_number) {
        previous_pattern_number = poss.pattern_number
        pattern_count = analyzed_possibilities
          .filter(val => val.pattern_number == poss.pattern_number)
          .length
        percentage_display = percent => Number.isFinite(percent) ? ((percent * 100).toPrecision(3) + '%') : '—'
        out_line += `<td rowspan=${pattern_count}>${percentage_display(poss.category_total_probability)}</td>`;
      }
      out_line += `<td>${percentage_display(poss.probability)}</td>`;
      for (let day of poss.prices.slice(1)) {
        if (day.min !== day.max) {
          out_line += `<td>${day.min} ${i18next.t("output.to")} ${day.max}</td>`;
        } else {
          out_line += `<td>${day.min}</td>`;
        }
      }
      out_line += `<td>${poss.weekGuaranteedMinimum}</td><td>${poss.weekMax}</td></tr>`;
      output_possibilities += out_line
    }
  
    $("#output").html(output_possibilities)
  
    update_chart(data, analyzed_possibilities);
  }
  
  const generatePermalink = function (buy_price, sell_prices, first_buy, previous_pattern) {
    let searchParams = new URLSearchParams();
    let pricesParam = buy_price ? buy_price.toString() : '';
  
    if (!isEmpty(sell_prices)) {
      const filtered = sell_prices.map(price => isNaN(price) ? '' : price).join('.');
      pricesParam = pricesParam.concat('.', filtered);
    }
  
    if (pricesParam) {
      searchParams.append('prices', pricesParam);
    }
  
    if (first_buy) {
      searchParams.append('first', true);
    }
  
    if (previous_pattern !== -1) {
      searchParams.append('pattern', previous_pattern);
    }
  
    return searchParams.toString() && window.location.origin.concat('/turnip?', searchParams.toString());
  }
  
  const copyPermalink = function () {
    let text = permalink_input[0];
  
    permalink_input.show();
    text.select();
    text.setSelectionRange(0, 99999); /* for mobile devices */
  
    document.execCommand('copy');
    permalink_input.hide();
  
    flashMessage(i18next.t("prices.permalink-copied"));
  }
  
  const flashMessage = function(message) {
    snackbar.text(message);
    snackbar.addClass('show');
  
    setTimeout(function () {
      snackbar.removeClass('show')
      snackbar.text('');
    }, 3000);
  }
  
  const update = function () {
    const sell_prices = getSellPrices();
    const buy_price = parseInt(buy_input.val());
    const first_buy = getCheckedRadio(first_buy_radios) == 'true';
    const previous_pattern = parseInt(getCheckedRadio(previous_pattern_radios));
  
    buy_input[0].disabled = first_buy;
    buy_input[0].placeholder = first_buy ? '—' : '...'
  
    const permalink = generatePermalink(buy_price, sell_prices, first_buy, previous_pattern);
    if (permalink) {
      permalink_button.show();
    } else {
      permalink_button.hide();
    }
    permalink_input.val(permalink);
  
    const prices = [buy_price, buy_price, ...sell_prices];
  
    if (!window.populated_from_query) {
      updateLocalStorage(prices, first_buy, previous_pattern);
    }
  
    calculateOutput(prices, first_buy, previous_pattern);
  }
  