//Reusable Fields const getSellFields = function () { let fields = []; var now = new Date(); for (var i = 2; i < 14; i++) { fields.push($("#sell_" + i)[0]); if (i == now.getDay() * 2 + (now.getHours() >= 12 ? 1 : 0)) { fields[fields.length - 1].classList.add("now"); } } if (now.getDay() == 0) { buy_input[0].classList.add("now"); } 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 state = { initialized: false, }; const buy_input = $("#buy"); const sell_inputs = getSellFields(); 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(); } }); console.log('finished initializing'); state.initialized = true; }; 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. */ const 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 getPriceClass = function(buy_price, max) { const priceBrackets = [200, 30, 0, -30, -99]; let diff = max - buy_price; for(var i=0; i= priceBrackets[i]) { return "range" + i; } } return ""; }; const displayPercentage = function(fraction) { if (Number.isFinite(fraction)) { let percent = fraction * 100; if (percent >= 1) { return percent.toPrecision(3) + '%'; } else if (percent >= 0.01) { return percent.toFixed(2) + '%'; } else { return '<0.01%'; } } else { return '—'; } }; const hideChart = function() { $("#output").html(""); $(".chart-wrapper").hide() } const calculateOutput = function (data, first_buy, previous_pattern) { if (isEmpty(data)) { hideChart() return; } let pat_desc = {0:"fluctuating", 1:"large-spike", 2:"decreasing", 3:"small-spike", 4:"all"}; let output_possibilities = ""; let predictor = new Predictor(data, first_buy, previous_pattern); let analyzed_possibilities = predictor.analyze_possibilities(); if (analyzed_possibilities[0].weekGuaranteedMinimum === Number.POSITIVE_INFINITY) { hideChart() $(".error:hidden").show() return; } $(".error:visible").hide() $(".chart-wrapper:hidden").show() let buy_price = parseInt(buy_input.val()); previous_pattern_number = ""; for (let poss of analyzed_possibilities) { var out_line = "" + i18next.t("patterns." + pat_desc[poss.pattern_number]) + ""; const style_price = buy_price || poss.prices[0].min; 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; out_line += `${displayPercentage(poss.category_total_probability)}`; } out_line += `${displayPercentage(poss.probability)}`; for (let day of poss.prices.slice(2)) { let price_class = getPriceClass(style_price, day.max); if (day.min !== day.max) { out_line += `${day.min} ${i18next.t("output.to")} ${day.max}`; } else { out_line += `${day.min}`; } } var min_class = getPriceClass(style_price, poss.weekGuaranteedMinimum); var max_class = getPriceClass(style_price, poss.weekMax); out_line += `${poss.weekGuaranteedMinimum}${poss.weekMax}`; 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('?', 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 () { if(!state.initialized){ console.log('update function called before initial data load'); // calls to update before the previous data has been initialized / loaded will reset the data. return; } 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)); 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); };