QQOL

Quality of Quality of Life!

// ==UserScript==
// @name         QQOL
// @namespace    http://tampermonkey.net/
// @version      0.64
// @description  Quality of Quality of Life!
// @include *queslar.com/*
// @require https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js
// @grant        unsafeWindow
// ==/UserScript==

//mat-tab-label-0-1
//TODO HOOK INTO document.querySelector('app-gamecontent') observer


////
//var rootElement = getAllAngularRootElements()[0].children[1]["__ngContext__"][30];
//      storage = rootElement.playerGeneralService;
////


//0.64
// rootElement.playerCurrencyService.gem_fragments
//

//QQOL.gameData.marketService.serviceOrders
//

class FTGMod {
 constructor() {
   this.ver = '0.64';
   //OBSERVERS
   var modbody = this;
   this.serviceOrders = {};
   this.newActionObserver = new MutationObserver(function(mutations) {
     mutations.forEach(function(mutation) {
          modbody.OnNewAction();
     });
    });
    this.newActionObserver.observe(
      document.querySelector('head > title'),
      {subtree: true, characterData: true, childList: true }
    );
    this.onactionhooks = [];



    this.newTabObserver = new MutationObserver(function(mutations) {
      //APP-INVENTORY
      mutations.forEach(function(mutation) {
        if (mutation.target.nodeName.toLowerCase().includes('app-')) {
          if (mutation.addedNodes.length>0) {
            if (mutation.target.nodeName.toLowerCase().split('-').length==2) {
              if (mutation.target.nodeName.toLowerCase().split('-')[1]!='gamecontent') {
                let tab = mutation.target.nodeName.toLowerCase().split('-')[1];
                //crafting
                if (tab=='actions') {
                  let subtab = mutation.target.childNodes[2].nodeName.toLowerCase().split('-')[1];
                  if (subtab=='actions') {
                    subtab=='pets'; //Blah pls
                  }
                  modbody.currentTab = subtab;
                  modbody.OnNewTab(subtab);
                } else if (tab=='market') {
                  let subtab = mutation.target.childNodes[2].nodeName.toLowerCase().split('-')[2];
                  modbody.currentTab = subtab;
                  modbody.OnNewTab(subtab);
                } else {
                  modbody.currentTab = mutation.target.nodeName.toLowerCase().split('-')[1];
                  modbody.OnNewTab(mutation.target.nodeName.toLowerCase().split('-')[1]);
                }

            }
            }

          }
        }
      });
    });

   this.newTabObserver.observe(
     document.querySelector('app-gamecontent'),
     {subtree: true, childList: true }
   );
   this.ontabhooks = [];
   //I SUCK AT JS
   //document.onclick = function(){modbody.Update()} //im retarded, look up hooks later
                                                   //.clickable onclick?

   //NEW DOM
   var csselem = document.createElement("link");
   csselem.setAttribute("rel", "stylesheet");
   csselem.setAttribute("type", "text/css");
   csselem.setAttribute("href", "https://countto25.github.io/QueslarQQOL/cssfix.css");
   document.getElementsByTagName("head")[0].appendChild(csselem);

   let QQOLholder = document.createElement('div');
   QQOLholder.id = 'QQOL_holder';
   document.getElementById('profile-next-level').parentNode.insertBefore(QQOLholder,document.getElementById('profile-next-level').nextSibling);
   //document.getElementById('mat-tab-content-0-1').children[0].appendChild(QQOLholder);

   let QQOLinfo = document.createElement('div');
   QQOLinfo.style.marginTop = '10px';
   QQOLinfo.id='QQOL_info';
   QQOLinfo.innerHTML = '<mat-icon class="mat-icon material-icons" style="vertical-align: bottom: height: 16px; width: 16px; font-size: 16px">settings</mat-icon><span id="toSettings" class="QQOL-link-action">QQOL v'+this.ver+'</span>';

   let QQOLquests = document.createElement('div');
   QQOLquests.id='QQOL_quests';
   QQOLquests.innerHTML = 'Loading...';

   let QQOLkdexp = document.createElement('div');
   QQOLkdexp.id='QQOL_kingdomexploration';
   QQOLkdexp.innerHTML = '';


   let QQOLTimeToTargetLevel = document.createElement('div');
   QQOLTimeToTargetLevel.id='QQOL_TTTL';
   if (!localStorage.targetLevel || parseInt(localStorage.QQOL_tttl_show)!=1)
    QQOLTimeToTargetLevel.style.display = 'none';
   QQOLTimeToTargetLevel.innerHTML = '';

   let timetoleveluptooltip = document.createElement('div');
   timetoleveluptooltip.id = 'QQOL_time_to_levelup';
   let idletimeremainingtooltip = document.createElement('div');
   idletimeremainingtooltip.id='QQOL_remaining_time';
   document.getElementById('QQOL_holder').appendChild(QQOLinfo);
   document.getElementById('QQOL_holder').appendChild(idletimeremainingtooltip);
   document.getElementById('QQOL_holder').appendChild(timetoleveluptooltip);
   document.getElementById('QQOL_holder').appendChild(QQOLquests);
   document.getElementById('QQOL_holder').appendChild(QQOLTimeToTargetLevel);
   document.getElementById('QQOL_holder').appendChild(QQOLkdexp);


   //DECLARE SHIT
   this.rememberquest = null;
   this.activetab = null;
   this.updateInterval = null;

   //FINISH
   this.HookOnAction(() => {modbody.Update()});
   this.HookOnAction(() => {if (modbody.rememberquest!=null && document.title.split(' - ')[1]!='Party') modbody.rememberquest--;});
   this.HookOnAction(() => {if (modbody.updateInterval == null) {modbody.updateInterval = setInterval(() => {modbody.Update()}, 100)}});
   this.HookOnAction(() => this.IncomePerHour());

   this.HookOnTab((x) => {console.log(x)});
   this.HookOnTab((x) => {modbody.activetab = x});
   this.HookOnTab((x) => {if (x==='battle') this.BlockActionsOnOvercap()});

   setInterval(() => {modbody.CheckLatestVersion()}, 1000000);

   this.CheckLatestVersion();
   this.DoUI();
   this.SetupSettings();
   this.ReflectSettings();



   console.log('loaded Quality of Quality of Life mod v'+this.ver+'. Have a nice day!');
 }

 HookOnAction(func, exec=false) {
   if (exec) func();
   this.onactionhooks.push(func);
 }
 HookOnTab(func, exec=false) {
   if (exec) func();
   this.ontabhooks.push(func);
 }

 get gameData() {
   let rootElement = getAllAngularRootElements()[0].children[1]["__ngContext__"][30];
   return rootElement.playerGeneralService;

 }

 Update() {
   this.TimeRemaining();
   this.TimeToLevelUp();
   this.TimeToQuestComplete();
   this.ReflectTimeToTargetLevel();
   this.explorationTimer();
 }

 GetRemainingActions() {
   return this.gameData.playerActionService.actions.remaining;
 }

 OnNewAction() {
   for (let i=0; i<this.onactionhooks.length; i++) {
     this.onactionhooks[i]();
   }
 }

 OnNewTab(tname) {
   for (let i=0; i<this.ontabhooks.length; i++) {
     this.ontabhooks[i](tname);
   }
 }

 CreateTimerWindow() {
   if (document.querySelector('.h5.mt-1')) {
     let timerelement = document.createElement('span');
    timerelement.id = 'FTG_idle_timer';
    timerelement.classList.add('h5');
    timerelement.setAttribute('style','margin-top: .25rem !important;');
    document.querySelector('.h5.mt-1').parentNode.insertBefore(timerelement,document.querySelector('.h5.mt-1'))
  }
 }

 BlockActionsOnOvercap() {
   return '';
   //find smth to do with issue
   if (this.gameData.playerActionService.actions.remaining > this.gameData.playerActionService.actions.total &&
       this.gameData.playerActionService.currentSkill == "battling" &&
       !this.gameData.partyService.isFighting) {
     document.querySelector('[joyridestep="startingTutorialSix"]').style.pointerEvents = 'none';
     document.querySelector('[joyridestep="startingTutorialSix"]').innerHTML = 'Refreshing will reset action cap';
   } else {
     document.querySelector('[joyridestep="startingTutorialSix"]').style.pointerEvents = 'unset';
     document.querySelector('[joyridestep="startingTutorialSix"]').innerHTML = 'Fight';
   }
 }

 TimeRemaining() {
   let actionsRemaining = this.GetRemainingActions();
   let txt ='Idle time remaining: '+this.ActionsToTime(actionsRemaining);
   let playerId = this.gameData.gameService.playerData.id;
   if (this.gameData.partyService.hasParty) {
    let partyActionsRemaining = this.gameData.partyService.partyOverview.partyInformation[playerId].actions.daily_actions_remaining;
    actionsRemaining += partyActionsRemaining;
    //<span class="QQOL-tooltip">
    txt ='<span class="QQOL-tooltip"><span class="QQOL-tooltiptext">'
      +this.GetRemainingActions()+
      ' solo actions and '+partyActionsRemaining+' party actions</span>Idle time remaining</span>: '
      +this.ActionsToTime(actionsRemaining);
   }
   if (this.GetRemainingActions()<0) {
     txt ="<span style='color: red'>Restart your actions!</span>";
   }
   if (document.getElementById('QQOL_remaining_time'))
    document.getElementById('QQOL_remaining_time').innerHTML=txt;
 }

 explorationTimer()
 {
     //rootElement.playerGeneralService.playerKingdomService.kingdomData.selectedExploration

     if (!this.gameData.playerKingdomService.isInKingdom) {
         return;
     }

     if (!this.gameData.playerKingdomService.kingdomData) {
         return;
     }

     if (!this.gameData.playerKingdomService.kingdomData.selectedExploration) {
         return;
     }

     let exploration = this.gameData.playerKingdomService.kingdomData.selectedExploration;
     let timetoend = new Date(exploration.exploration_timer);
     timetoend = Math.floor(timetoend.getTime() / 1000);
     let now = new Date();
     now = Math.floor(now.getTime() / 1000);

     let diff = timetoend - now;
     let time = this.SecondsToString(diff);
     document.querySelector('#QQOL_kingdomexploration').innerHTML = 'KD exploration: '+time;
 }

 GetSeconds(actions) {
   return '';
 }

 IncomePerHour() {
  let infospan;
  let subname = (this.currentTab == 'party')?'p':'s'
  if (!document.querySelector('#QQOL_GEPH_'+subname)) {
    let appendTo = document.querySelector('.action-result-value-container');
    infospan = document.createElement('div');
    infospan.style.marginTop = '10px';
    infospan.id='QQOL_GEPH_'+subname;
    if (appendTo)
      appendTo.appendChild(infospan);
  } else {
    infospan = document.querySelector('#QQOL_GEPH_'+subname);
  }
  if (this.currentTab == 'party') {
    if (Object.keys(this.gameData.partyService.actionResult).length!=0) {
      let exp = this.gameData.partyService.actionResult.income.experience.amount;
      let gold = this.gameData.partyService.actionResult.income.gold.amount;
      infospan.innerHTML = `(<span class='QQOL-tooltip'>${(gold*600).toLocaleString()} gold and ${(exp*600).toLocaleString()} experience per hour<span class='QQOL-tooltiptext'>Unless you die</span></span>)`;
    }
  } else {
    if (Object.keys(this.gameData.playerActionService.actionResult).length!=0) {
      let exp = this.gameData.playerActionService.actionResult.income.experience.amount;
      let gold = this.gameData.playerActionService.actionResult.income.gold.amount;
      infospan.innerHTML = `(<span class='QQOL-tooltip'>${(gold*600).toLocaleString()} gold and ${(exp*600).toLocaleString()} experience per hour<span class='QQOL-tooltiptext'>Unless you die</span></span>)`;
    }
  }
 }


 TimeToQuestComplete() {
   let txt;
   let cQuest = this.gameData.playerQuestService.currentQuest[0];
   if (this.gameData.playerQuestService.currentQuestId!=0) {
     if (cQuest.objectiveType=='actions') {
       let remaining = cQuest.objectiveAmount - cQuest.currentProgress;
       txt = 'Time to quest completion: '+this.ActionsToTime(remaining);
    } else if (cQuest.objectiveType=='kills') {
      if (this.gameData.playerActionService.currentSkill=='battling') {
        txt = '<span class="QQOL-tooltip">Time to quest completion<span class="QQOL-tooltiptext">If you keep on fighting solo and avoiding death</span></span>: '
          +this.ActionsToTime(cQuest.objectiveAmount - cQuest.currentProgress)
      } else {
        txt = 'Kills quest active, not in battle';
      }
    } else if (cQuest.objectiveType=='gold') {
        if (this.gameData.playerActionService.currentSkill=='battling') {
          let gpt = 0;
          if (!this.gameData.partyService.isFighting) {
            gpt = this.gameData.playerActionService.actionResult.income.gold.amount;
            if (this.gameData.playerActionService.actionResult.income.gold.tax)
              gpt+=this.gameData.playerActionService.actionResult.income.gold.tax;
          } else {
              txt = 'Doing party actions';
              if (document.querySelector('#QQOL_quests'))
               document.querySelector('#QQOL_quests').innerHTML=txt;
              return false;
          }
          let actionsToCompletion = Math.ceil((cQuest.objectiveAmount - cQuest.currentProgress)/gpt);
          txt =
            '<span class="QQOL-tooltip">Time to quest completion<span class="QQOL-tooltiptext">At '
            +gpt+' gold per turn</span></span>: '+this.ActionsToTime(actionsToCompletion);
      } else {
        txt = 'Gold quest active, not in battle';
      }
    }
   } else {
     txt = '<span style="color:red">Grab a new quest!</span>';
   }
   if (document.querySelector('#QQOL_quests'))
    document.querySelector('#QQOL_quests').innerHTML=txt;
  }

 TimeToLevelUp() {
   if (document.getElementById('profile-next-level')) {
   let txt = document.getElementById('profile-next-level').innerHTML;
   let actionVal = parseInt(txt.replace(/\D/g,''));
   txt='Time to next level: '+this.ActionsToTime(actionVal);
   if (document.getElementById('QQOL_time_to_levelup'))
    document.getElementById('QQOL_time_to_levelup').innerHTML = txt;
    }

 }

 ExpToLevel() {
   let expBank = 0;
   let currentLevel = this.gameData.playerLevelsService.battling.level;
   if (!localStorage.getItem('targetLevel')) return false;
   let targetLevel = parseInt(localStorage.getItem('targetLevel'));
   let currentExp = this.gameData.playerLevelsService.battling.exp.have;
   if (targetLevel == 0) return false;
   for (let i=currentLevel; i<targetLevel; i++) {
     let expToLevel = Math.round(25000 * Math.pow(i, 0.5));
     let levelTemp = i;
     while (levelTemp > 1500) {
       expToLevel +=  250 * Math.pow((levelTemp - 1500), 1.25)
       levelTemp -= 1500;
     }
     expBank+=expToLevel;
   }
   return expBank-currentExp;
 }

 TimeToTargetLevel() {
   if (!localStorage.getItem('targetLevel')) {
     return 'Time to target level: check settings';
   }
   let actionVal;
   if (this.gameData.playerActionService.actionResult.income && this.gameData.playerActionService.actionResult.income.experience.amount) {
      actionVal = this.gameData.playerActionService.actionResult.income.experience.amount;
   } else {return '';}
   let totalExpReq = this.ExpToLevel();
   let actionsReq = Math.ceil(totalExpReq/actionVal);
   let tLevel = localStorage.targetLevel;
   return 'Time to level '+tLevel+': '+this.ActionsToTime(actionsReq);
 }

 ReflectTimeToTargetLevel() {
   let div = document.getElementById('QQOL_TTTL');
   if (!div) return;
   div.innerHTML = this.TimeToTargetLevel();
 }

 FindProvider() {
   let sTerm = document.getElementById('QQOL_service_search').value.toLowerCase();
   let users = document.querySelectorAll('td.cdk-column-username.mat-column-username > div');
   for (let i=0; i<users.length; i++) {
     if (users[i].innerHTML.toLowerCase().includes(sTerm)) {
       users[i].parentNode.parentNode.removeAttribute('style');
     } else {
       users[i].parentNode.parentNode.style.display='none'
     }
   }
 }
 ActionsToTime(actions) {
   if (actions<0) return '00:00';
   let minval = Math.floor(actions/10);
   let hourval = Math.floor(minval/60);
   let remMinutes = minval%60;
   let remSeconds = actions/10
   let subSeconds =  this.gameData.partyService.isFighting?this.gameData.partyService.countDown:this.gameData.playerActionService.countDown;
   if ((actions*6%60) - (6-subSeconds)  < 0)
    remMinutes--;

    let remSec = actions*6%60;
    let a = 6-subSeconds;
    if (remSec-a<0) {remSec=remSec+60-a} else {remSec = remSec-a;}
    if (remSec<10) remSec='.0'+remSec;
    else remSec='.'+remSec;

   let dayVal = Math.floor(hourval/24);
   return ((dayVal>0)?(dayVal+'d '):(''))+hourval%24+':'+(remMinutes<10?('0'+remMinutes):(remMinutes))+remSec;
 }



 SecondsToString(seconds)
{
  var numyears = Math.floor(seconds / 31536000);
  var numdays = Math.floor((seconds % 31536000) / 86400);
  var numhours = Math.floor(((seconds % 31536000) % 86400) / 3600);
  var numminutes = Math.floor((((seconds % 31536000) % 86400) % 3600) / 60);
  var numseconds = Math.floor((((seconds % 31536000) % 86400) % 3600) % 60);
  return numhours.toString().padStart(2, '0') + ":" + numminutes.toString().padStart(2, '0') + "." + numseconds.toString().padStart(2, '0');
}




 get myEquipmentData() {
   let myID = this.gameData.gameService.playerData.id;
   return this.gameData.partyService.partyOverview.partyInformation[myID].equipment;
 }

  IsPlayerRelated(name) {
    if (this.playerRelatives.includes(name))
      return true;
    else
      return false;
 }



  CheckLatestVersion() {
    let modbody = this;
    var request = new XMLHttpRequest();
    request.open('GET', 'https://api.github.com/repos/countto25/queslarqqol/tags', true);
    request.onload = function() {
      if (request.status >= 200 && request.status < 400) {
        var data = JSON.parse(request.responseText);
        console.log(data);
        let latestVersion = data[1].name;
        console.log(parseFloat(latestVersion) + ' vs ' +parseFloat(modbody.ver));
        console.log(modbody.ver);
        console.log(parseFloat(latestVersion) > parseFloat(modbody.ver));
        if ((parseFloat(latestVersion) > parseFloat(modbody.ver))) {
          let txt = 'QQOL v'+modbody.ver+'. <a target="_blank" href="https://countto25.github.io/QueslarQQOL?update=true" class="QQOL-link-action" style="color:red; text-decoration: none">Please update</a>';
          document.querySelector('#toSettings').innerHTML=txt;
        } else if (parseFloat(latestVersion) < parseFloat(modbody.ver)) {
          let txt = 'QQOL v'+modbody.ver+'. <span style="color:green; text-decoration: none">Maybe do your actual job?</span>';
          document.querySelector('#toSettings').innerHTML=txt;
        }
      } else {console.log('error getting new version :')}
    }
    request.send();
  }

  fetchHTML(url) {
    if( 'undefined' == typeof(url) ) return false;
    let p;
    if( document.all ){
      p = new ActiveXObject("Microsoft.XMLHTTP");
    } else {
    p = new XMLHttpRequest();
    }
    let rnd = Math.random().toString().substring(3);
    if( url.indexOf('?') > -1 )
    {
      url+='&rnd='+rnd;
    }
    else
    {
      url+='?rnd='+rnd;
    }
    p.open("GET",url,false);
    p.send(null);
    return p.responseText;
}

  DoUI() {
    let div = document.createElement("div");
    let settingsmenu = this.fetchHTML('https://countto25.github.io/QueslarQQOL/menu.html');
    div.classList.add("QQOLsettings");
    div.style.display = 'none';
    div.innerHTML = settingsmenu;
    let modbody = this;
    document.body.appendChild(div);
    document.querySelector('#exitSettings').onclick = function() {
      document.querySelector('.QQOLsettings').style.display='none';
      modbody.ReflectSettings();
    }
    document.querySelector('#contactme').onclick = function() {
      document.querySelector('.chat-input ').value='/w FiammaTheGreat';
      document.querySelector('.QQOLsettings').style.display='none';
      modbody.ReflectSettings();
    }
    document.querySelector('#toSettings').onclick = function() {
      document.querySelector('.QQOLsettings').style.display='block';
    }
    let checks = document.querySelectorAll('input[type=checkbox].QQOLCheck');
    checks.forEach(check => {
      check.oninput = function() {
        localStorage.setItem('QQOL_'+this.getAttribute('for'), this.checked?1:0);
        console.log(this.checked?1:0);
      }
    });
    document.querySelector('input[for=tttl_value]').oninput = function() {
      localStorage.setItem('targetLevel', this.value);
    }
  }
  SetupSettings() {
    if (localStorage.getItem('QQOL_itr_show')===null) {
      localStorage.setItem('QQOL_itr_show', 1);
    }
    if (localStorage.getItem('QQOL_nl_show')===null) {
      localStorage.setItem('QQOL_nl_show', 1);
    }
    if (localStorage.getItem('QQOL_ttqc_show')===null) {
      localStorage.setItem('QQOL_ttqc_show', 1);
    }

    if (localStorage.getItem('QQOL_tttl_show')===null) {
      localStorage.setItem('QQOL_tttl_show', 0);
    }
    if (localStorage.getItem('QQOL_ttke_show')===null) {
        localStorage.setItem('QQOL_ttke_show', 0);
    }

    if (localStorage.getItem('QQOL_itr_show') === '0') {
      document.querySelector('input[for=itr_show]').checked = false;
    } else {
      document.querySelector('input[for=itr_show]').checked = true;
    }
    if (localStorage.getItem('QQOL_nl_show') === '0') {
      document.querySelector('input[for=nl_show]').checked = false;
    } else {
      document.querySelector('input[for=nl_show]').checked = true;
    }

    if (localStorage.getItem('QQOL_ttqc_show') === '0') {
      document.querySelector('input[for=ttqc_show]').checked = false;
    } else {
      document.querySelector('input[for=ttqc_show]').checked = true;
    }

    if (localStorage.getItem('QQOL_tttl_show') === '0') {
      document.querySelector('input[for=tttl_show]').checked = false;
    } else {
      document.querySelector('input[for=tttl_show]').checked = true;
    }

    if (localStorage.getItem('QQOL_ttke_show') === '0') {
      document.querySelector('input[for=ttke_show]').checked = false;
    } else {
      document.querySelector('input[for=ttke_show]').checked = true;
    }

    let tl = (localStorage.getItem('targetLevel') || -1);
    if (tl != -1) {
      document.querySelector('input[for=tttl_value]').value = parseInt(tl);
    }
  }

  ReflectSettings() {
    if (localStorage.getItem('QQOL_itr_show') === '0') {
      document.querySelector('#QQOL_remaining_time').style.display = 'none';
    } else {
      document.querySelector('#QQOL_remaining_time').style.display = 'block';
    }

    if (localStorage.getItem('QQOL_nl_show') === '0') {
      document.querySelector('#QQOL_time_to_levelup').style.display = 'none';
    } else {
      document.querySelector('#QQOL_time_to_levelup').style.display = 'block';
    }

    if (localStorage.getItem('QQOL_ttqc_show') === '0') {
      document.querySelector('#QQOL_quests').style.display = 'none';
    } else {
      document.querySelector('#QQOL_quests').style.display = 'block';
    }

    if (localStorage.getItem('QQOL_ttke_show') === '0') {
      document.querySelector('#QQOL_kingdomexploration').style.display = 'none';
    } else {
      document.querySelector('#QQOL_kingdomexploration').style.display = 'block';
    }

    if (localStorage.getItem('QQOL_tttl_show') === '0') {
      document.querySelector('#QQOL_TTTL').style.display = 'none';
    } else {
      document.querySelector('#QQOL_TTTL').style.display = 'block';
    }

  }

  AdvanceFromCurrent(exp) {
    let projectedLevel = this.gameData.playerLevelsService.battling.level;
    while (exp > 0) {
      let expToLevel = Math.round(25000 * Math.pow(projectedLevel, 0.5));
      let levelTemp = projectedLevel;
      while (levelTemp > 1500) {
        expToLevel +=  250 * Math.pow((levelTemp - 1500), 1.25)
        levelTemp -= 1500;
      }
      projectedLevel++;
      exp-=expToLevel;
    }
    return projectedLevel;
  }
 }


//TY GREASEMONKEY
var QQOL = null;
console.log('init load');
var QQOLSetupInterval = setInterval(QQOLGMSetup, 1000);
function QQOLGMSetup() {
  if (document.getElementById('profile-next-level')&&QQOL===null) {
    console.log('load OK');
    clearInterval(QQOLSetupInterval);
    console.log(QQOLSetupInterval+'');
    QQOL = new FTGMod();

  } else {
    console.log('retry init...');
    console.log('next level?'+document.getElementById('profile-next-level'));
    console.log((document.getElementById('profile-next-level')&&QQOL===null));
  }
}