Greasy Fork

来自缓存

Greasy Fork is available in English.

MH - Rank-up Forecaster (v2.0)

Records wisdom data over time and predict rank-ups!

当前为 2021-07-02 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         MH - Rank-up Forecaster (v2.0)
// @version      1.0.9
// @description  Records wisdom data over time and predict rank-ups!
// @author       Chromatical
// @match        https://www.mousehuntgame.com/*
// @match        https://apps.facebook.com/mousehunt/*
// @icon         https://www.google.com/s2/favicons?domain=mousehuntgame.com
// @require      https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.3.2/chart.min.js
// @require      https://cdn.jsdelivr.net/npm/chartjs-adapter-date-fns@next/dist/chartjs-adapter-date-fns.bundle.min.js
// @grant        GM_addStyle
// @grant        GM_getResourceText
// @namespace http://greasyfork.icu/users/748165
// ==/UserScript==
var debug = localStorage.getItem("Chro.debug") == 1? true : false;

//Data--------------------------
const title = ['Novice','Recruit','Apprentice','Initiate','Journeyman','Master','Grandmaster','Legendary','Hero','Knight','Lord','Baron','Count','Duke','Grand Duke','Archduke','Viceroy','Elder','Sage','Fabled'];
const wisdomRequired = [0,2000,5000,12500,31250,65440,137813,303188,667013,1467428,3228341,7102349,15625168,34375370,75625813,166376789,366028936,805263659,1771580048,3897476106];
const currentTitle = user.title_name;
const currentPercent = user.title_percent_accurate;
const nextTitle = title[title.indexOf(currentTitle)+1];
//Data end-----------------------
(function(){
    addEventListener();
    /*global chart*/

   GM_addStyle(`
    .forecaster-chart-wrapper {
      position: fixed;
      left: 36vw;
      top: 48vh;
      background: rgba(255, 255, 255, 1);
      border: thin solid grey;
      z-index: 9999
    }
  `);
})()

$(document).ajaxComplete(async(event,xhr,options) => {
    if(options.url == "https://www.mousehuntgame.com/managers/ajax/users/userInventory.php" ||
      options.url =="https://www.agiletravels.com/uuid.php"||
      options.url == "https://www.agiletravels.com/intake.php" ||
      options.url == "https://www.mousehuntgame.com/managers/ajax/users/getmiceeffectiveness.php"
      ){
         if (debug) console.log("WF - URL useless, ignored")
    } else if (options.url == "https://www.mousehuntgame.com/managers/ajax/users/changeenvironment.php"){
        const promise1 = await wisdomCheck("bypass")
        .then(
            res =>{
                if (debug) console.log("Travel so all bypassed and recorded!")
            })
    } else {
        var storageTime = localStorage.getItem("Chro-forecaster-time");
        const promise = await wisdomCheck()
        .then(
            res =>{
                if (debug) console.log("WF-Check passed! Recording Now")
            },
            err =>{
                if (debug) console.log("WF-Not time yet!")
            })
        }
});

function wisdomCheck(status){
    return new Promise(async(resolve,reject) =>{
        var storageTime = localStorage.getItem("Chro-forecaster-time")
        if (storageTime === null){
            var storageTimeParsed = [];
            var last = 0;
            var lastGetTime = 0;
            var currentTime = new Date()
        } else {
            storageTimeParsed = JSON.parse(storageTime)
            last = storageTimeParsed.length -1;
            lastGetTime = storageTimeParsed[last][0]
            currentTime = new Date()
        }
        if (storageTimeParsed.length <4){
            var interval = 3600000
            } else if (storageTimeParsed.length <8){
                interval = 43200000
            } else if (storageTimeParsed.length <12){
                interval = 86400000
            } else {
                interval = 172800000
            }
        if (debug) interval = 120000;
        var previousTimeISO = (new Date(lastGetTime)).getTime()
        var currentTimeISO = currentTime.getTime()
        if (debug) console.log("WF - Interval Differene is " + (currentTimeISO - previousTimeISO))
        if (currentTimeISO - previousTimeISO >= interval || status == "bypass"){
            if (debug && status != "bypass") console.log ("WF - Bigger Interval");
            if (status == "bypass") console.log ("Time check bypassed")
            const promise = await getWisdom()
            .then(
                async res=>{
                    var wisdom = res;
                    //if (status != "bypass"){
                        var oldArray = storageTimeParsed;
                       oldArray.push([currentTime,wisdom]);
                      //  while (oldArray.length > 14){
                        //    oldArray.shift()
                       // }
                        localStorage.setItem("Chro-forecaster-time",JSON.stringify(oldArray));
                    //}
                    const promise2 = await getTotalCalls()
                    .then(
                        res=>{
                            var calls = res;
                            var callArray = localStorage.getItem("Chro-forecaster-current-area")
                            if (callArray){
                                var parsedCallArray = JSON.parse(callArray);
                                //If same name with the current Area
                                if (parsedCallArray[0] == user.environment_name || status == "bypass"){
                                    if (status == "bypass") console.log ("Area checked bypassed")
                                    //Check whether the difference between now and past horns > 0
                                    if (calls-parsedCallArray[2] > 0) {
                                        //Check whether there is a global Area
                                        var areaArray = localStorage.getItem("Chro-forecaster-all-area");
                                        //If got global area
                                        if (areaArray){
                                            var parsedAreaArray = JSON.parse(areaArray)
                                            //Check whether there has already been records of current area
                                            var i = 0;
                                            while (i<parsedAreaArray.length){
                                                if (debug) console.log("WF-There are past records of current area")
                                                //By bypass
                                                if (status == "bypass"){
                                                    if (parsedAreaArray[i][0] == parsedCallArray[0]){
                                                        if (debug) console.log ("WF-Deleting bypass past data");
                                                        parsedAreaArray.splice(i,1);
                                                    } else {
                                                        i++
                                                    }
                                                 //Not by bypass
                                                } else {
                                                    if (parsedAreaArray[i][0] == user.environment_name){
                                                        if (debug) console.log ("WF-Deleting past data");
                                                        parsedAreaArray.splice(i,1);
                                                    } else {
                                                        i++
                                                    }
                                                }
                                            }
                                            if (debug) console.log ("WF-Writing new data")
                                            parsedAreaArray.push([parsedCallArray[0],wisdom-parsedCallArray[1],calls-parsedCallArray[2]])
                                            localStorage.setItem("Chro-forecaster-all-area",JSON.stringify(parsedAreaArray))
                                        //If no global array, write one
                                        } else {
                                            if (debug) console.log("WF-Writing new global array")
                                            var newAreaArray = [[parsedCallArray[0],wisdom-parsedCallArray[1],calls-parsedCallArray[2]]]
                                            localStorage.setItem("Chro-forecaster-all-area",JSON.stringify(newAreaArray))
                                        }
                                    } else if (debug) {console.log("WF-Hunt difference too small")}
                                    if (status == "bypass"){
                                        //bypassed so still need to re-record area
                                        console.log ("bypassed check, so re-record")
                                        callArray = [user.environment_name,wisdom,calls]
                                        localStorage.setItem("Chro-forecaster-current-area",JSON.stringify(callArray))
                                    }
                                } else {
                                    // If different area, re-record
                                    if (debug) console.log ("WF-Area changed! Recording new Area")
                                    callArray = [user.environment_name,wisdom,calls]
                                    localStorage.setItem("Chro-forecaster-current-area",JSON.stringify(callArray))
                                }
                            } else {
                                // if no call array
                                callArray = [user.environment_name,wisdom,calls]
                                localStorage.setItem("Chro-forecaster-current-area",JSON.stringify(callArray))
                            }
                        })
                    resolve()
                }
            )
            } else {
                reject(this)
            }
    })
}

function getWisdom(){
    return new Promise(async(resolve,reject) =>{
        postReq("https://www.mousehuntgame.com/managers/ajax/users/userInventory.php",`sn=Hitgrab&hg_is_ajax=1&item_types%5B%5D=wisdom_stat_item&action=get_items&uh=${user.unique_hash}`)
        .then(res=>{
            try{
                var data = JSON.parse(res.responseText);
                if (data){
                    var wisdom = data.items[0].quantity
                    resolve(wisdom);
                    }
            } catch (error){
                console.log(error)
            }
        })
    })
}

function getTotalCalls(){
    return new Promise(async(resolve,reject) =>{
        postReq("https://www.mousehuntgame.com/managers/ajax/users/userData.php",`sn=Hitgrab&hg_is_ajax=1&uh=${user.unique_hash}&sn_user_ids%5B%5D=${user.sn_user_id}&fields%5B%5D=num_total_turns`)
        .then(res=>{
            try{
                var data = JSON.parse(res.responseText);
                if (data){
                    var snuid = user.sn_user_id;
                    var calls = data.user_data[snuid].num_total_turns //parseInt(data.page.tabs.profile.subtabs[0].num_total_horn_calls.replace(",","").match(/\d+/g))
                    resolve(calls);
                    }
            } catch (error){
                console.log(error)
            }
        })
    })
}

function addEventListener(){
    var clickPoint;
    ($(".mousehuntHud-userStat-row.points>.label")[0])? clickPoint = $(".mousehuntHud-userStat-row.points>.label")[0] : clickPoint = $(".hudstatlabel")[7];
    //const clickPoint = $(".mousehuntHud-userStat-row.points>.label")[0]
    clickPoint.style.cursor = "pointer"
    clickPoint.addEventListener("click",()=>{
        forecastBox();
    })
}

async function forecastBox(){
    document
     .querySelectorAll("#chro-forecast-div")
     .forEach(el=> el.remove())

    const mainDiv = document.createElement("div")
    mainDiv.id = "chro-forecast-div"
    mainDiv.style.cursor = "default"
    $(mainDiv).css({
        "background-color": "#F5F5F5",
        "position": "fixed",
        "z-index": "9999",
        "left": "35vw",
        "top": "20vh",
        "border": "solid 3px #696969",
        "border-radius": "20px",
        "padding": "10px",
        "text-align": "center",
        "font-size": "12px",
        "min-width": "177px"
    })

    const headerDiv = document.createElement("div")
    headerDiv.id = "main-header-div"
    headerDiv.innerText = "Rank-up Forecaster"
    $(headerDiv).css({
        "float": "left",
        "font-weight":"bold",
        "width":"120px",
        "padding-top": "4px"
    })

    const contentDiv = document.createElement("div");
    contentDiv.id = "forecaster-content-div"
    $(contentDiv).css({
        "padding-top":"6px"
    })


    const btnDiv = document.createElement("div")
    btnDiv.id = "forecast-button-Div"
    $(btnDiv).css({
    })

    const minBtn = document.createElement("button")
    minBtn.id ="forecast-min-btn"
    minBtn.className = "forecaster-main-btn"
    minBtn.innerText = "-"
    minBtn.onclick = () =>{
        if (minBtn.innerText == "-"){
            contentDiv.style.display = "none"
            minBtn.innerText = "+"
        } else {
            contentDiv.style.display = ""
            minBtn.innerText = "-"
        }
    }

    const closeBtn = document.createElement("button")
    closeBtn.id = "forecast-close-btn"
    closeBtn.className = "forecaster-main-btn"
    closeBtn.innerText = "x"
    closeBtn.onclick = ()=>{
        document.body.removeChild(mainDiv);
        var historybox = $("#chro-forecast-history-div")[0]
        var canvasbox = $(".forecaster-chart-wrapper")[0]
        var locationbox = $("#chro-forecast-location-div")[0]
        if(historybox) document.body.removeChild($("#chro-forecast-history-div")[0]);
        if(canvasbox) document.body.removeChild($(".forecaster-chart-wrapper")[0]);
        if(locationbox) document.body.removeChild($("#chro-forecast-location-div")[0]);
    };
    [$(minBtn),$(closeBtn)].forEach(el=>{el.css({"margin-left":"5px"})});

    const titleTable = document.createElement("table");
    titleTable.id = "forecaster-table"
    $(titleTable).css({
        "border-spacing":"6px 3px"
    })

    const currentRow = document.createElement("tr")
    const currentHeaderData = document.createElement("td");
    currentHeaderData.innerText = "Current:"
    const currentTitleData = document.createElement("td");
    currentTitleData.innerText = currentTitle + " (" + currentPercent + "%)"

    const wisdomRow = document.createElement("tr")
    const wisdomHeaderData = document.createElement("td");
    wisdomHeaderData.innerText = "Wisdom:"
    const data = localStorage.getItem("Chro-forecaster-time")
    if(data){
        const parsedData = JSON.parse(data)
        var last = parsedData.length-1
        var wisdom = parsedData[last][1]
        } else {
            wisdom = 0
        }
    const wisdomTitleData = document.createElement("td");
    wisdomTitleData.innerText = numberWithCommas(wisdom);

    const nextRow = document.createElement("tr");
    const nextHeaderData = document.createElement("td");
    nextHeaderData.innerText = "Predict:"
    const nextTitleData = document.createElement("td");
    const titleList = document.createElement("select")
    titleList.style.width = "103px"
    titleList.id = "chro-forecaster-title-list"
    var a = title.indexOf(currentTitle)
    title.forEach(el=>{
        if (title.indexOf(el)>a || el == "Fabled"){
            var options = document.createElement("OPTION");
            options.innerText = el;
            titleList.appendChild(options);
        }
    });
    var selectedRank = titleList.value;
    var pointsRequired = wisdomRequired[title.indexOf(titleList.value)]
    titleList.onchange = async function processTitle(e){
        selectedRank = titleList.value;
        pointsRequired = wisdomRequired[title.indexOf(titleList.value)]
        var text = await getPredictDate(pointsRequired)
        .then(
           res => {
               $("#Chro-forecaster-forecasted-data")[0].innerText = res;
               if ($("#my-canvas")[0]){
                   renderChart(pointsRequired,selectedRank)
               }
           })
    }
    nextTitleData.appendChild(titleList);

    const timeRow = document.createElement("tr")
    const timeHeaderData = document.createElement("td");
    timeHeaderData.innerText = "Time:"
    const timeTitleData = document.createElement("td");
    timeTitleData.id = "Chro-forecaster-forecasted-data"
    timeTitleData.style.width = "10px"
    var p = await getPredictDate(pointsRequired)
    .then(
        res=> {timeTitleData.innerText = res}
        );
    //timeTitleData.innerText = inner_text;

     [$(currentHeaderData),$(wisdomHeaderData),$(nextHeaderData),$(timeHeaderData)].forEach(el=>{el.css({
         "font-weight": "bold",
         "text-align" : "right"
     })});

    const contButtonDiv = document.createElement("div")
    contButtonDiv.id = "forecast-content-button-div"

    const historyBtn = document.createElement("button")
    historyBtn.id = "forecast-history-button"
    historyBtn.className = "forecaster-content-button"
    historyBtn.innerText = "History"
    historyBtn.onclick = () => {
        if($("#chro-forecast-history-div")[0]){
            document.body.removeChild($("#chro-forecast-history-div")[0])
        } else {
        renderHistoryBox()
        }
    }

    const chartBtn = document.createElement("button")
    chartBtn.id = "forecast-history-button"
    chartBtn.className = "forecaster-content-button"
    chartBtn.innerText = "Chart"
    chartBtn.style.marginLeft = "5px";
    chartBtn.onclick = async () => {
        if($(".forecaster-chart-wrapper")[0]){
            document.body.removeChild($(".forecaster-chart-wrapper")[0])
        } else {
        var p = await renderChart(pointsRequired,selectedRank).then(
            res => {
                var x = $("#Chro-forecaster-chart")[0];
                dragElement(x,x)
            })
        }
    }

    const locationBtn = document.createElement("button")
    locationBtn.id = "forecast-location-button"
    locationBtn.className = "forecaster-content-button"
    locationBtn.innerText = "Location"
    locationBtn.style.marginLeft = "5px";
    locationBtn.onclick = async () => {
        if($("#chro-forecast-location-div")[0]){
            document.body.removeChild($("#chro-forecast-location-div")[0])
        } else {
            renderLocationBox()
        }
    }

    btnDiv.appendChild(minBtn)
    btnDiv.appendChild(closeBtn)
    mainDiv.appendChild(headerDiv)
    mainDiv.appendChild(btnDiv)
    currentRow.appendChild(currentHeaderData)
    currentRow.appendChild(currentTitleData)
    wisdomRow.appendChild(wisdomHeaderData)
    wisdomRow.appendChild(wisdomTitleData)
    nextRow.appendChild(nextHeaderData)
    nextRow.appendChild(nextTitleData)
    timeRow.appendChild(timeHeaderData)
    timeRow.appendChild(timeTitleData)
    titleTable.appendChild(currentRow)
    titleTable.appendChild(wisdomRow)
    titleTable.appendChild(nextRow)
    titleTable.appendChild(timeRow)
    contentDiv.appendChild(titleTable)
    contButtonDiv.appendChild(historyBtn);
    contButtonDiv.appendChild(chartBtn);
    contButtonDiv.appendChild(locationBtn);
    contentDiv.appendChild(contButtonDiv);
    mainDiv.appendChild(contentDiv)
    document.body.appendChild(mainDiv)
    dragElement(mainDiv,headerDiv)
}

function renderLocationBox(){
    document
     .querySelectorAll("#chro-forecast-location-div")
     .forEach(el=> el.remove())

    const locationDiv = document.createElement("div")
    locationDiv.id = "chro-forecast-location-div"
    $(locationDiv).css({
        "background-color": "#F5F5F5",
        "position": "fixed",
        "z-index": "9999",
        "left": "52vw",
        "top": "20vh",
        "border": "solid 3px #696969",
        "border-radius": "20px",
        "padding": "10px",
        "text-align": "center",
        "font-size": "12px",
        "min-width": "177px"
    })
    const locationMainTable = document.createElement("table")
    locationMainTable.id = "location-table"
    $(locationMainTable).css({
        "border-spacing": "1em 6px",
        "border-collapse": "collapse",
    })

    const placeHeading = document.createElement("th")
    placeHeading.id = "Chro-forecaster-place-heading"
    placeHeading.innerText = "Location";
    const wisdomHeading = document.createElement("th")
    wisdomHeading.id = "Chro-forecaster-location-wisdom-heading"
    wisdomHeading.innerText = "Wisdom/Hunt";
    const percentHeading = document.createElement("th")
    percentHeading.id = "Chro-forecaster-location-percent-heading"
    percentHeading.innerText = "%+/Hunt";
    const huntNumberHeading = document.createElement("th")
    huntNumberHeading.id = "Chro-forecaster-location-hunt-heading"
    huntNumberHeading.innerText = "Hunts";

    [$(placeHeading),$(wisdomHeading),$(percentHeading),$(huntNumberHeading)].forEach(el=>{el.css({
         "font-weight": "bold",
         "text-align" : "center",
         "background-color": "#eaeef0",
         "padding": "3px",
         "padding-top": "3px",
         "padding-bottom": "3px",
         "border":"0.5px solid #696969",
     })});

    locationMainTable.appendChild(placeHeading);
    locationMainTable.appendChild(wisdomHeading);
    locationMainTable.appendChild(percentHeading);
    locationMainTable.appendChild(huntNumberHeading);

    const locationData = localStorage.getItem("Chro-forecaster-all-area")
    if (locationData){
        var parsedLocationData = JSON.parse(locationData);
        for (var i=0;i<parsedLocationData.length;i++){
            var row = document.createElement("tr")
            var location_data = document.createElement("td")
            location_data.innerText = parsedLocationData[i][0]
            var hunt_data = document.createElement("td");
            var total_wisdom = parsedLocationData[i][1];
            var total_hunts = parsedLocationData[i][2];
            hunt_data.innerText = (total_wisdom/total_hunts).toFixed(2)
            var percent_data = document.createElement("td");
            var a = wisdomRequired[title.indexOf(currentTitle)];
            percent_data.innerText = ((total_wisdom/total_hunts)/a).toFixed(6) + "%";
            var hunt_number_data = document.createElement("td");
            hunt_number_data.innerText = total_hunts;

            if (i%2 ==0){
            [$(location_data),$(hunt_data),$(percent_data),$(hunt_number_data)].forEach(el=>{el.css({
            "text-align":"right",
            "border":"0.5px solid #696969",
            "padding":"3px",
            })})
        } else {
            [$(location_data),$(hunt_data),$(percent_data),$(hunt_number_data)].forEach(el=>{el.css({
            "text-align":"right",
            "border":"0.5px solid #696969",
            "padding":"3px",
            "background-color":"white"
            })})
        }
            row.appendChild(location_data);
            row.appendChild(hunt_data);
            row.appendChild(percent_data)
            row.appendChild(hunt_number_data);
            locationMainTable.appendChild(row)
        }
    }
    locationDiv.appendChild(locationMainTable)
    document.body.appendChild(locationDiv)
    dragElement(locationDiv,locationDiv)
}

function renderChart(pointsRequired,rank){
 return new Promise((resolve, reject) => {
    document
     .querySelectorAll("#my-canvas")
     .forEach(el=> el.remove())

    const parsedData = JSON.parse(localStorage.getItem("Chro-forecaster-time"));
    const timeData = [];
    const wisdomData = [];
    parsedData.forEach(el=>{
        var d = new Date(el[0])
        var date_converted = d.getTime();
        timeData.push(date_converted)
    });
    parsedData.forEach(el=>{wisdomData.push(el[1])});

    const createElement = (tagName, config = {}) => {
        const el = document.createElement(tagName);
        if (config.attrs) Object.entries(config.attrs).forEach(([attr, val]) => el.setAttribute(attr, val));
        if (config.props) Object.entries(config.props).forEach(([prop, val]) => el[prop] = val);
        if (config.css) Object.entries(config.css).forEach(([prop, val]) => el.style[prop] = val);
        if (config.children) config.children.forEach(child => el.append(child));
            return el;
        };

    function main() {
        const chartMain = document.body.prepend(createElement('div', {
            props: {
                className: 'forecaster-chart-wrapper',
                id: 'Chro-forecaster-chart'
            },
            children: [
                createElement('canvas', {
                    props: {
                        height: "200",
                        width: "400"
                    },
                    attrs: { id: 'my-canvas' }
                })
            ]
        }));

        const historyChartData = []
        for (var i=0;i<wisdomData.length;i++){
            historyChartData.push({
                x: timeData[i],
                y: wisdomData[i]
            });
        }

        const bfChartData = []
        bfChartData.push({
            x:timeData[0],
            y:wisdomData[0]
        });
        var last = timeData.length-1;
        var find_wisdom = findLineByLeastSquares(timeData,wisdomData)
        var y_value = find_wisdom[0]*(timeData[last])+find_wisdom[1]
        bfChartData.push({
            x: timeData[last],
            y: y_value
        });

        const predictChartData = []
        predictChartData.push({
            x: timeData[0],
            y: wisdomData[0]
        });
        predictChartData.push({
            x: findLineByLeastSquares(timeData,wisdomData,pointsRequired)[2],
            y: pointsRequired
        });

        const chartData = [{
            label: 'History',
            data: historyChartData,
            borderWidth: 1,
            fill: false,
            borderColor: '#25c9f6',
            backgroundColor: '#4dc9f6',
            tension: 0.1,
        },{
            label: 'Best Fit',
            data: bfChartData,
            borderWidth: 1,
            fill: false,
            borderColor: '#9966FF',
            backgroundColor: '#9966FF',
            tension: 0.1
        },{
            label: rank,
            data: predictChartData,
            borderWidth: 1,
            fill: false,
            borderColor: '#953553',
            backgroundColor: '#953553',
            tension: 0.1
        }];

        const myChart = createSimpleLineChart('#my-canvas', chartData);

    }

    function createChart (canvas, settings) {
        new Chart((typeof canvas === 'string' ? document.querySelector(canvas) : canvas).getContext('2d'), settings)
    };

    function createSimpleLineChart (selector, chartData){
        const {data,label} = chartData;
        return createChart(selector, {
            type: 'line',
            data: {datasets: chartData},
            options: {
                scales: {
                    yAxes: [{
                        ticks: {
                            beginAtZero: false,
                        }
                    }],
                    x: {
                        type: 'time',
                    }
                },
            },
            plugins: {
                datalabels: {
                    color: 'blue'
                }
            }
        });
    };
    main();
    resolve();
 })
};

function renderHistoryBox(){
    document
     .querySelectorAll("#chro-forecast-history-div")
     .forEach(el=> el.remove())

    const historyDiv = document.createElement("div")
    historyDiv.id = "chro-forecast-history-div"
    $(historyDiv).css({
        "background-color": "#F5F5F5",
        "position": "fixed",
        "z-index": "9999",
        "left": "18vw",
        "top": "20vh",
        "border": "solid 3px #696969",
        "border-radius": "20px",
        "padding": "10px",
        "text-align": "center",
        "font-size": "12px",
        "min-width": "177px"
    })
    const historyMainTable = document.createElement("table")
    historyMainTable.id = "history-table"
    $(historyMainTable).css({
        "border-spacing": "1em 6px",
        "border-collapse": "collapse",
    })

    const tableBody = document.createElement("tbody");

    const data = localStorage.getItem("Chro-forecaster-time")
    const parsedData = JSON.parse(data)

    const timeHeading = document.createElement("th")
    timeHeading.id = "Chro-forecaster-time-heading"
    timeHeading.innerText = "Date  " + String.fromCharCode("0x23F7");
    timeHeading.style.cursor = "pointer";
    timeHeading.onclick = function(){
        if (timeHeading.innerText == "Date ⏷"){
            timeHeading.innerText = "Date  " + String.fromCharCode("0x23F6")
        } else {
            timeHeading.innerText = "Date  " + String.fromCharCode("0x23F7")
        };
        reverse(tableBody);
    };
    const wisdomHeading = document.createElement("th")
    wisdomHeading.id = "Chro-forecaster-wisdom-heading"
    wisdomHeading.innerText = "Wisdom+";
    const percentageHeading = document.createElement("th")
    percentageHeading.id = "Chro-forecaster-percentage-heading"
    percentageHeading.innerText = "%";
    const percentageIncreaseHeading = document.createElement("th")
    percentageIncreaseHeading.id = "Chro-forecaster-percentage-increase-heading"
    percentageIncreaseHeading.innerText = "%+";
    const removeHeading = document.createElement("th")
    removeHeading.id = "Chro-forecaster-remove-heading";

    [$(timeHeading),$(wisdomHeading),$(percentageHeading),$(percentageIncreaseHeading),$(removeHeading)].forEach(el=>{el.css({
         "font-weight": "bold",
         "text-align" : "center",
         "background-color": "#eaeef0",
         "padding": "3px",
         "padding-top": "3px",
         "padding-bottom": "3px",
         "border":"0.5px solid #696969",
     })});

    historyMainTable.appendChild(timeHeading)
    historyMainTable.appendChild(wisdomHeading)
    //historyMainTable.appendChild(percentageHeading)
    historyMainTable.appendChild(percentageIncreaseHeading)

    const dataLength = parsedData.length
    for (var i=0;i<parsedData.length;i++){
        const row = document.createElement("tr")
        row.id = "row"+i
        const time_value = parsedData[i][0]
        const wisdom_value = parsedData[i][1]

        var d = new Date(time_value)
        var hunt_date = formatDate(d)//d.getDate() + '/' + (d.getMonth()+1) //+ '/' + d.getFullYear()
        var hunt_time = ("0" + d.getHours()).slice(-2) + ":" + ("0" + d.getMinutes()).slice(-2)

        const time_data = document.createElement("td")
        time_data.className = "Chro-forecaster-time-data"
        time_data.innerText = hunt_date + " " + hunt_time

        const wisdom_data = document.createElement("td")
        wisdom_data.className = "Chro-forecaster-wisdom-data"
        var previous_wisdom = parsedData[Math.max(i-1,0)][1]
        var wisdom_difference = wisdom_value-previous_wisdom
        wisdom_data.innerText = numberWithCommas(wisdom_difference);
        $(wisdom_data).css({"text-align":"right"})

        const percentage_data = document.createElement("td")
        percentage_data.innerText = (wisdom_value/(wisdomRequired[title.indexOf(currentTitle)+1])*100).toFixed(2)+ "%"

        const percentage_increase_data = document.createElement("td")
        var percentage_increase = ((wisdom_value-previous_wisdom)*100/(previous_wisdom)).toFixed(2)
        percentage_increase_data.innerText = percentage_increase + "%"

        const remove_btn = document.createElement("button")
        remove_btn.innerText = "x";
        remove_btn.onclick =()=>{
            for (var i=0;i<parsedData.length;i++){
                var d = new Date(parsedData[i][0])
                var hunt_date = formatDate(d);//d.getDate() + '/' + (d.getMonth()+1) //+ '/' + d.getFullYear()
                var hunt_time = ("0" + d.getHours()).slice(-2) + ":" + ("0" + d.getMinutes()).slice(-2)
                var remove_time = hunt_date + " " + hunt_time
                if(remove_time == remove_btn.parentElement.children[0].innerText){
                    parsedData.splice([i],1);
                    tableBody.removeChild(remove_btn.parentElement);
                }
            }
            var unparsed_data = JSON.stringify(parsedData)
            localStorage.setItem("Chro-forecaster-time",unparsed_data);
        }
        $(remove_btn).css({
            "width": "15px",
            "height": "15px",
            "font-size": "10px",
            "text-align": "center",
            "padding": "0px",
            "background-color":"#F5F5F5",
            "border":"none",
            "border-color":"#F5F5F5",
            "cursor":"pointer"
        });
        if (i%2 ==0){
            [$(time_data),$(wisdom_data),$(percentage_increase_data)].forEach(el=>{el.css({
            "text-align":"right",
            "border":"0.5px solid #696969",
            "padding":"3px",
            })})
        } else {
            [$(time_data),$(wisdom_data),$(percentage_increase_data)].forEach(el=>{el.css({
            "text-align":"right",
            "border":"0.5px solid #696969",
            "padding":"3px",
            "background-color":"white"
            })})
        }

        row.appendChild(time_data)
        row.appendChild(wisdom_data)
        //row.appendChild(percentage_data)
        row.appendChild(percentage_increase_data)
        row.appendChild(remove_btn)
        tableBody.appendChild(row)
    }
    historyMainTable.appendChild(tableBody)
    historyDiv.appendChild(historyMainTable)
    document.body.appendChild(historyDiv)

    dragElement(historyDiv,historyDiv);
    reverse(tableBody);

}

function getPredictDate(points){
    return new Promise((resolve, reject) => {
        const parsedData = JSON.parse(localStorage.getItem("Chro-forecaster-time"));
        if (parsedData){
        const timeData = [];
        const wisdomData = [];
        parsedData.forEach(el=>{
            var d = new Date(el[0])
            var date_converted = d.getTime();
            timeData.push(date_converted)
        });
        parsedData.forEach(el=>{wisdomData.push(el[1])});
        //function end --------------
          var timeRequired = findLineByLeastSquares(timeData,wisdomData,points)[2]
        var d = new Date(timeRequired);
        resolve(d);
        } else {
            d = "Insufficient Data"
        }
    })
}

function formatDate(d){
    var allMonths = ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]
    var month = d.getMonth()+1
    var monthFormat = allMonths[Number(month) -1]
    var dateFormat = (d.getFullYear()).toString().substr(-2);
    return(d.getDate() + ' ' + monthFormat + ' ' + dateFormat);
}

function reverse(table){
    $(table).each(function(elem,index){
            var arr = $.makeArray($("tr",this).detach());
            arr.reverse();
            $(this).append(arr);
        });
}

function numberWithCommas(x) {
    return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}

function findLineByLeastSquares(values_x, values_y,rank_value) {
    var x_sum = 0;
    var y_sum = 0;
    var xy_sum = 0;
    var xx_sum = 0;
    var count = 0;

    /*
     * The above is just for quick access, makes the program faster
     */
    var x = 0;
    var y = 0;
    var values_length = values_x.length;

    if (values_length != values_y.length) {
        throw new Error('The parameters values_x and values_y need to have same size!');
    }

    //Above and below cover edge cases
    if (values_length === 0) {
        return [ [], [] ];
    }

    //Calculate the sum for each of the parts necessary.
    for (var i = 0; i< values_length; i++) {
        x = values_x[i];
        y = values_y[i];
        x_sum+= x;
        y_sum+= y;
        xx_sum += x*x;
        xy_sum += x*y;
        count++;
    }

    // y = m*x + b
    var m = (count*xy_sum - x_sum*y_sum) / (count*xx_sum - x_sum*x_sum);
    var b = (y_sum/count) - (m*x_sum)/count;

    //x = (y-b)/m
    var time_value = (rank_value - b)/m

    return [m, b,time_value];
}


function dragElement(elmnt,dragEl) {
  var pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
  /*if (elmnt.firstElementChild) {
    // if present, the header is where you move the DIV from:
   elmnt.firstElementChild.onmousedown = dragMouseDown;
  } else {*/
    // otherwise, move the DIV from anywhere inside the DIV:
    dragEl.onmousedown = dragMouseDown;
  //}

  function dragMouseDown(e) {
    e = e || window.event;
    e.preventDefault();
    // get the mouse cursor position at startup:
    pos3 = e.clientX;
    pos4 = e.clientY;
    document.onmouseup = closeDragElement;
    // call a function whenever the cursor moves:
    document.onmousemove = elementDrag;
  }

  function elementDrag(e) {
    e = e || window.event;
    e.preventDefault();
    // calculate the new cursor position:
    pos1 = pos3 - e.clientX;
    pos2 = pos4 - e.clientY;
    pos3 = e.clientX;
    pos4 = e.clientY;
    // set the element's new position:
    elmnt.style.top = (elmnt.offsetTop - pos2) + "px";
    elmnt.style.left = (elmnt.offsetLeft - pos1) + "px";
  }

  function closeDragElement() {
    // stop moving when mouse button is released:
    document.onmouseup = null;
    document.onmousemove = null;
  }
}

function postReq(url, form) {
    return new Promise((resolve, reject) => {
      const xhr = new XMLHttpRequest();
      xhr.open("POST", url, true);
      xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
      xhr.onreadystatechange = function () {
        if (this.readyState === XMLHttpRequest.DONE && this.status === 200) {
          resolve(this);
        }
      };
      xhr.onerror = function () {
        reject(this);
      };
      xhr.send(form);
    });
}