Harvest festival 2024.png

Dare to try out the Harvest Festival 2024!?

MediaWiki:VanaTime.js: Difference between revisions

From HorizonXI Wiki
No edit summary
m (Undo revision 58733 by Hugin8484 (talk))
Tag: Undo
 
(113 intermediate revisions by 3 users not shown)
Line 1: Line 1:
//*********************************************************************
/*********************************************************************
// Javascript below contributes to live Vana'diel Time listed in Sidebar
Javascript below contributes to Vana'diel Time displays throughout
// All code taken from: https://www.pyogenes.com/ffxi/timer/v2.html  
the HorizonXI Wiki
//***************************
Credits: ********
https://www.pyogenes.com/ffxi/timer/v2.html  
https://www.mithrapride.org/vana_time/index.html
https://www.rubydoc.info/gems/vanadiel-time/Vanadiel/Time
**********************************************************************/


/*
class timeElements {
Add the class tags below to incorporate current guild times
// List of all class names for each element in this model, for styling
<b><p class="page-alchemy_hours"></p></b>


The following are valid class tags:
static sidebar = "n-vanatime";
page-alchemy_hours
    static conquest = "vanatime-page-conquest-schedule"; // <span ... />
page-bonecraft_hours
    //static moonPhase = "vanatime-page-moon-phase";  // not implemented
page-clothcraft_hours
    static moonSchedule = "vanatime-page-moon-schedule-table";
page-cooking_hours
    static rseSchedule = "vanatime-page-rse-schedule-table";
page-fishing_hours
   
page-goldsmithing_hours
    static airships = {
page-leathercraft_hours
        //all : "vanatime-airship-schedule-table",    // not implemented
page-smithing_hours
        jeuno : "vanatime-airship-schedule-jeuno-table",
page-woodworking_hours
        bastok : "vanatime-airship-schedule-bastok-table",
        sandy : "vanatime-airship-schedule-sandy-table",
        windy : "vanatime-airship-schedule-windy-table",
        kazham : "vanatime-airship-schedule-kazham-table",
    };
 
    static boats = {
        selbina : "vanatime-boat-schedule-selbina-table",
        mhaura : "vanatime-boat-schedule-mhaura-table",
        bibiki : "vanatime-boat-schedule-bibiki-table",
        purgonorgoIsle : "vanatime-boat-schedule-purgonorgoIsle-table",
        northLanding : "vanatime-boat-schedule-northLanding-table",
        centralLanding : "vanatime-boat-schedule-centralLanding-table",
        southLanding : "vanatime-boat-schedule-southLanding-table"
    };
 
    static guilds = {
        alchemy : "vanatime-guild-schedule-alchemy",
        alchemy_status:  "vanatime-guild-schedule-alchemy-status",
        alchemy_holiday: "vanatime-guild-schedule-alchemy-holiday",
        alchemy_timer: "vanatime-guild-schedule-alchemy-timer",
 
        smithing : "vanatime-guild-schedule-smithing",
        smithing_status:  "vanatime-guild-schedule-smithing-status",
        smithing_holiday: "vanatime-guild-schedule-smithing-holiday",
        smithing_timer: "vanatime-guild-schedule-smithing-timer",
 
        bonecrafting : "vanatime-guild-schedule-bonecrafting",
        bonecrafting_status:  "vanatime-guild-schedule-bonecrafting-status",
        bonecrafting_holiday: "vanatime-guild-schedule-bonecrafting-holiday",
        bonecrafting_timer: "vanatime-guild-schedule-bonecrafting-timer",
 
        goldsmithing : "vanatime-guild-schedule-goldsmithing",
        goldsmithing_status:  "vanatime-guild-schedule-goldsmithing-status",
        goldsmithing_holiday: "vanatime-guild-schedule-goldsmithing-holiday",
        goldsmithing_timer: "vanatime-guild-schedule-goldsmithing-timer",
 
        clothcrafting : "vanatime-guild-schedule-clothcrafting",
        clothcrafting_status:  "vanatime-guild-schedule-clothcrafting-status",
        clothcrafting_holiday: "vanatime-guild-schedule-clothcrafting-holiday",
        clothcrafting_timer: "vanatime-guild-schedule-clothcrafting-timer",
 
        woodworking : "vanatime-guild-schedule-woodworking",
        woodworking_status:  "vanatime-guild-schedule-woodworking-status",
        woodworking_holiday: "vanatime-guild-schedule-woodworking-holiday",
        woodworking_timer: "vanatime-guild-schedule-woodworking-timer",
 
        leathercrafting : "vanatime-guild-schedule-leathercrafting",
        leathercrafting_status:  "vanatime-guild-schedule-leathercrafting-status",
        leathercrafting_holiday: "vanatime-guild-schedule-leathercrafting-holiday",
        leathercrafting_timer: "vanatime-guild-schedule-leathercrafting-timer",
 
        fishing : "vanatime-guild-schedule-fishing",
        fishing_status:  "vanatime-guild-schedule-fishing-status",
        fishing_holiday: "vanatime-guild-schedule-fishing-holiday",
        fishing_timer: "vanatime-guild-schedule-fishing-timer",
 
        cooking : "vanatime-guild-schedule-cooking",
        cooking_status:  "vanatime-guild-schedule-cooking-status",
        cooking_holiday: "vanatime-guild-schedule-cooking-holiday",
        cooking_timer: "vanatime-guild-schedule-cooking-timer",
 
        all : "vanatime-guild-schedule-all"
    };
}
 
 
class schedule {
    //  All raw datea is from ASB
    //  route definintion = [ 'Name of route', anim_arrive, anim_depart, timeOffset, time_interval, time_anim_arrive, time_waiting, time_anim_depart]
    //  Actual arrival time (when the player can enter the transport ) = time_offset + time_anim_arrive = [3] + [5]
    //  Departure time = arrival time + time_waiting = arrival_time + [6]
    //  All values listed are in VanaTime minutes, so 1440 is the total minutes in a game hour
   
        static #airship_jeuno_sandy = ['Jeuno -> Sandoria',  0,  360, 12, 60, 12];
        static #airship_jeuno_windy = ['Jeuno -> Windurst',  90,  360, 12, 60, 12];
        static #airship_jeuno_bastok = ['Jeuno -> Bastok',    180, 360, 12, 60, 16];
        static #airship_jeuno_kazham = ['Jeuno -> Kazham',    270, 360, 20, 50, 20];
        static #airship_bastok_jeuno = ['Bastok -> Jeuno',    0,  360, 14, 60, 16];
        static #airship_sandy_jeuno = ['Sandoria -> Jeuno',  180, 360, 12, 60, 16];
        static #airship_windy_jeuno = ['Windurst -> Jeuno',  270, 360, 18, 60, 14];
        static #airship_kazham_jeuno = ['Kazham -> Jeuno',    90,  360, 20, 50, 20];
   
        static airships = {
            jeuno : [
                this.#airship_jeuno_bastok,
                this.#airship_jeuno_sandy,
                this.#airship_jeuno_windy,
                this.#airship_jeuno_kazham,
                this.#airship_bastok_jeuno,
                this.#airship_sandy_jeuno,
                this.#airship_windy_jeuno,
                this.#airship_kazham_jeuno
            ],
            bastok :    [ this.#airship_bastok_jeuno, this.#airship_jeuno_bastok],
            sandy :    [this.#airship_sandy_jeuno, this.#airship_jeuno_sandy],
            windy :    [this.#airship_windy_jeuno, this.#airship_jeuno_windy],
            kazham :    [this.#airship_kazham_jeuno, this.#airship_jeuno_kazham]
        }
   
        static #boat_selbina_mhaura = ['Selbina -> Mhaura',    382, 480, 18, 80, 17];
        static #boat_mhaura_selbina = ['Mhaura -> Selbina',    382, 480, 18, 80, 17];
        static #boat_mhaura_whitegate = ['Mhaura -> Whitegate', 142, 480, 18, 80, 17];
   
        static #boat_whitegate_mhaura = ['Whitegate -> Mhaura', 142, 480, 18, 80, 16];
        static #boat_whitegate_nashmau = ['Whitegate -> Nashmau', 282, 480, 18, 180, 17];
        static #boat_nashmau_whitegate = ['Nashmau -> Whitegate', 282, 480, 18, 180, 17];
   
        static #boat_bibiki_tours = ['Bibiki Bay -> Tours', 710, 720, 20, 40, 20];
        static #boat_bibiki_purgo = ['Bibiki Bay -> Purgonorgo Isle', 270, 720, 20, 40, 20];
        static #boat_purgo_bibiki = ['Purgonorgo Isle -> Bibiki Bay', 500, 720, 20, 40, 20];
   
        static #boat_barge_south_central_emfa = ['South Landing -> Central Landing EMFEA', 5, 1440, 15, 35, 15];
        static #boat_barge_central_south_newtpool1 = ['Central Landing -> South Landing NewtPool', 267, 1440, 12, 30, 15];
        static #boat_barge_south_oos = ['South Landing -> OOS', 1402, 1440, 33,  0,  0];
        static #boat_barge_south_north = ['South Landing -> North Landing', 560, 1440, 15, 35, 15];
        static #boat_barge_north_oos = ['North Landing -> OOS', 925, 1440, 40,  0,  0];
        static #boat_barge_north_central = ['North Landing -> Central Landing',  993, 1440, 12, 40, 15];
        static #boat_barge_central_south_newtpool2 = ['Central Landing -> South Landing NewtPool 2', 1148, 1440, 12, 30, 15];
        static #boat_barge_south_oos1 = ['South Landing -> OOS 2', 512, 1440, 33,  0,  0];
   
        static boats = {
            selbina :  [ this.#boat_selbina_mhaura ],
            mhaura :  [ this.#boat_mhaura_selbina, this.#boat_mhaura_whitegate ],
            whitegate : [ this.#boat_whitegate_mhaura, this.#boat_whitegate_nashmau],
            nashmau : [ this.#boat_nashmau_whitegate ],
            bibiki :  [ this.#boat_bibiki_tours, this.#boat_bibiki_purgo],
            purgonorgoIsle : [ this.#boat_purgo_bibiki ],
            northLanding : [ this.#boat_barge_north_oos, this.#boat_barge_north_central ],
            centralLanding : [ this.#boat_barge_central_south_newtpool1, this.#boat_barge_central_south_newtpool2 ],
            southLanding : [ this.#boat_barge_south_central_emfa, this.#boat_barge_south_oos, this.#boat_barge_south_north, this.#boat_barge_south_oos1 ]
        }
   
        static #alchemy = [480, 1380, 5];
        static #smithing = [480, 1380, 2];
        static #bonecrafting = [480, 1380, 3];
        static #goldsmithing = [480, 1380, 4];
        static #clothcrafting = [360, 1260, 0];
        static  #woodworking = [360, 1260, 0];
        static #leathercrafting = [180, 1080, 4];
        static #fishing = [180, 1080, 5];
        static #cooking = [300, 1200, 7];
   
        static guilds = {
            // [ Open, Close, Holiday]
            alchemy : this.#alchemy,
            smithing : this.#smithing,
            bonecrafting : this.#bonecrafting,
            goldsmithing : this.#goldsmithing,
            clothcrafting : this.#clothcrafting,
            woodworking : this.#woodworking,
            leathercrafting : this.#leathercrafting,
            fishing : this.#fishing,
            cooking : this.#cooking,
            all : [ this.#alchemy, this.#smithing, this.#bonecrafting,this.#goldsmithing, this.#clothcrafting,this.#woodworking,this.#leathercrafting, this.#fishing, this.#cooking ]
        }
   
}
 
class VanaTime{
   
    #elementalDay =    ["Firesday",    "Earthsday",        "Watersday",        "Windsday",        "Iceday",          "Lightningday",    "Lightsday",    "Darksday"];
    #dayColor =        ["#FF0000",    "#AAAA00",          "#0000DD",          "#00AA22",        "#7799FF",          "#AA00AA",          "#AAAAAA",      "#333333"];
    #moonPhaseName =    ["New Moon",    "Waxing Crescent",  "First Quarter",    "Waxing Gibbous",  "Full Moon",        "Waning Gibbous",  "Last Quarter", "Waning Crescent"];
    #moonIcon =        ['\u{1F311}',  '\u{1F312}',        '\u{1F313}',        '\u{1F314}',        '\u{1F315}',        '\u{1F316}',        '\u{1F317}',    '\u{1F318}'];
    #moonPercentages =  ["(10%-0%-5%)", "(7%-38%)",        "(40%-55%)",        "(57%-88%)",        "(90%-100%-95%)",    "(93%-62%)",      "(60%-45%)",    "(43%-12%)"];
    #month =            ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
    #weekday =          ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
    #rseRace =          ["M. Hume","F. Hume","M. Elvaan","F. Elvaan","M. TaruTaru","F. TaruTaru","Mithra","Galka"];
    #rseLocation =      ["Gusgen Mines","Shakrami Maze","Ordelle Caves"];
 
   
    // Conversions in Milliseconds
    //#baseDate; // built at runtime: 1024844400000
    #moonDate =    1074997872000; // in milliseconds
    #VTIME_BIRTH    = 1024844400000; // vana birthday - in milliseconds
    #VTIME_BASEDATE  = 1009810800;  // unix epoch - 1009810800 = se epoch (in earth seconds)
    rseDate =      1075281264000;
 
    #ONE_SECOND = 1000000;
    #ONE_MINUTE;
    #ONE_HOUR;
    #ONE_DAY;
    // #ONE_WEEK;
    // #ONE_MONTH;
    // #ONE_YEAR;
 
    // Conversions in Minutes
    #VTIME_YEAR  =      518400;  // 360 * GameDay
    #VTIME_MONTH =      43200;      // 30 * GameDay
    #VTIME_WEEK  =      11520;      // 8 * GameDay
    #VTIME_DAY  =      1440;      // 24 hours * GameHour
    #VTIME_HOUR  =      60;        // 60 minutes
    #VMULTIPLIER =      25;
    #MOON_CYCLE_DAYS =  84;
    // #MOON_CYCLE_PHASE = 17280;    // 1 moon phase cycle = 12 vana days = 12 * Gameday
 
    /*
        Some of the math...
 
        (((898 * 360) + 30) * 24 * 60 * 60) / (this.#VMULTIPLIER / 1000)..... 
        converts vana time to earth time by ( / 25 ) then getting to milliseconds ( * 1000 )
    */
    #vanaBirthday = (((898 * 360) + 30) * 24 * 60 * 60) / (25 / 1000); // 1117359360000 - in earth time milliseconds
    #difference = this.#vanaBirthday - this.#VTIME_BIRTH; // 92514960000 - earth time milliseconds
 
    getDifference(){ return this.#difference };
 
    /**
    * Common variables for quick reference to current Vana time
    * @returns - integer / number
    */
    // vana_currentTime_inEarthMS = ((898 * 360 + 30) * (24 * 60 * 60 * 1000)) + (this.earthDate.getTime() - this.#VTIME_BIRTH) * this.#VMULTIPLIER; // vana time, represented in earth milliseconds - used for making Date() objects
    // vana_currentTime = this.vana_currentTime_inEarthMS / (60 * 1000); // vana time in minutes
    // //vana_currentTime = ( ((this.earthDate.getTime() / 1000) - this.#VTIME_BASEDATE ) / 60.0 * this.#VMULTIPLIER) + (886 * this.#VTIME_YEAR); //returned in vana minutes
    // vana_year = this.vana_currentTime / this.#VTIME_YEAR;
    // vana_month  = (this.vana_currentTime / this.#VTIME_MONTH) % 12 + 1;
    // vana_date = (this.vana_currentTime / this.#VTIME_DAY) % 30 + 1;
    // vana_weekday  = Math.floor((this.vana_currentTime % this.#VTIME_WEEK) / this.#VTIME_DAY);
    // vana_hour = (this.vana_currentTime % this.#VTIME_DAY) / this.#VTIME_HOUR;
    // vana_mins  = this.vana_currentTime % this.#VTIME_HOUR;
    // vana_moonphase  = Math.floor((this.vana_currentTime % this.#MOON_CYCLE_PHASE) / this.#VTIME_DAY);
 
    /**
    * Helper functions for quick reference to any Vana time
    * @returns - integer / number
    */
    now_inEarthMS(){
        var now = new Date();
        return ((898 * 360 + 30) * (24 * 60 * 60 * 1000)) + (now.getTime() - this.#VTIME_BIRTH) * this.#VMULTIPLIER;
    }


*/
    now(){ return this.now_inEarthMS() / ( 60 * 1000); }


//The following class tags are for the fully formatted time schedules
    now_inMS(){
const conquest_schedule = "vanatime-page-conquest-schedule";
        var timenow = new Date();
const airship_schedule = "vanatime-page-airship-schedule";
        return ((timenow.getTime() - this.#VTIME_BIRTH) % (24 * 60 * 60 * 1000 / 25));
const ferry_schedule = "vanatime-page-ferry-schedule";
    }
const moon_phase = "vanatime-page-moon-phase";
const moon_phases_schedule = "vanatime-page-moon-phases-schedule";
const crafting_guilds_schedule = "vanatime-page-crafting-guilds-schedule";


////////////////////////////////////////////////////////////////////
    now_inMINS(){
        return this.now_inMS / ( 1000 * 60 );
    }


basisDate = new Date();
    today_inMS(){ // result in earth Milliseconds
basisDate.setUTCFullYear(2002, 5, 23); // Set date to 2003-06-23
        var now = this.now_inEarthMS();
basisDate.setUTCHours(15, 0, 0, 0);   // Set time to 15:00:00.0000
        return ( now - ( now % (24 * 60 * 60 * 1000) ));
Mndate = new Date();
    }
Mndate.setUTCFullYear(2004, 0, 25); // Set date to 2004-01-25
 
Mndate.setUTCHours(2, 31, 12, 0);   // Set time to 02:31:12.0000
    year(vanatime) {
msGameDay = (24 * 60 * 60 * 1000 / 25); // milliseconds in a game day
        if (vanatime === undefined || vanatime == null) vanatime = this.now();
msRealDay = (24 * 60 * 60 * 1000); // milliseconds in a real day
        return Math.floor(vanatime / this.#VTIME_YEAR);
VanaDay = new Array("Firesday", "Earthsday", "Watersday", "Windsday", "Iceday", "Lightningday", "Lightsday", "Darksday");
    }
DayColor = new Array("#FF0000", "#AAAA00", "#0000DD", "#00AA22", "#7799FF", "#AA00AA", "#AAAAAA", "#333333");
 
//PhaseName = new Array("Full Moon","Waning Gibbous","Last Quarter","Waning Crescent","New Moon","Waxing Crescent","First Quarter","Waxing Gibbous");
    month(vanatime){
PhaseName = new Array('\u{1F315}','\u{1F316}','\u{1F317}','\u{1F318}','\u{1F311}','\u{1F312}','\u{1F313}','\u{1F314}');
        if (vanatime === undefined || vanatime == null) vanatime = this.now();
sMonth = new Array("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec");
        return Math.floor((vanatime / this.#VTIME_MONTH) % 12) + 1;
RSErace = new Array("M. Hume","F. Hume","M. Elvaan","F. Elvaan","M. TaruTaru","F. TaruTaru","Mithra","Galka");
    }
RSEloc = new Array("Gusgen Mines","Shakrami Maze","Ordelle Caves");
 
BoatSched = new Array("08:00", "16:00", "00:00");
    date(vanatime){
BoatSched2 = new Array("06:30", "14:30", "22:30");
        if (vanatime === undefined || vanatime == null) vanatime = this.now();
BoatDayOffset = new Array(0,0,7);
        return Math.floor((vanatime / this.#VTIME_DAY) % 30) + 1;
    }
   
function resetGameTime() {  
    weekDay(vanatime){
        if (vanatime === undefined || vanatime == null) vanatime = this.now();
// **** Game time
        return Math.floor((vanatime % this.#VTIME_WEEK) / this.#VTIME_DAY);
var now = new Date();
    }
vanaDate = ((898 * 360 + 30) * msRealDay) + (now.getTime() - basisDate.getTime()) * 25;
 
    hour(vanatime){
        if (vanatime === undefined || vanatime == null) vanatime = this.now();
        return Math.floor((vanatime % this.#VTIME_DAY) / this.#VTIME_HOUR);
    }
 
    mins(vanatime){
        if (vanatime === undefined || vanatime == null) vanatime = this.now();
        return Math.floor(vanatime % this.#VTIME_HOUR);
    }
 
    dayColor(vanatime){
        if (vanatime === undefined || vanatime == null) vanatime = this.now();
        return this.#dayColor[this.weekDay()];
    }
   
    dayLabel(day){  
        if (day === undefined || day == null) return this.#elementalDay[this.weekDay()];
        else return this.#elementalDay[day]
    }
 
    moonPhaseIcon(day){
        if (day === undefined || day == null) return this.#moonIcon[this.moonLatentPhase()];
        else return this.#moonIcon[day];
    }
 
    moonPhaseName(day){
        if (day === undefined || day == null) return this.#moonPhaseName[this.moonLatentPhase()];
        else return this.#moonPhaseName[day];
    }
 
    moonPhasePercentages(day){
        if (day === undefined || day == null) return this.#moonPercentages[this.moonLatentPhase()];
        else return this.#moonPercentages[day];
    }
 
    /**   
    * Time Helper functions - supports string generation for month/week details
    * @param  (required) - represents which RACE
    * @returns - string - from #rseRace or #rseLocation
    */
    monthLabel(m){ return this.#month[m]; }
    weekdayLabel(w){ return this.#weekday[w]; }
 
    /**   
    * RSE Helper functions - supports string generation for RSE details
    * @param r (required) - represents which RACE
    * @returns - string - from #rseRace or #rseLocation
    */
    rseRace(r){ return this.#rseRace[r]; }
 
    /**   
    * RSE Helper functions - supports string generation for RSE details
    * @param r (required) - represents which RACE
    * @returns - string - from #rseRace or #rseLocation
    */
    rseLocation(r){ return this.#rseLocation[r]; }
 
    constructor(){
        this.#ONE_MINUTE = 60  * this.#ONE_SECOND;
        this.#ONE_HOUR = 60  * this.#ONE_MINUTE;
        this.#ONE_DAY = 24  * this.#ONE_HOUR;
        // this.#ONE_WEEK = 8  * this.#ONE_DAY;
        // this.#ONE_MONTH = 30  * this.#ONE_DAY;
        // this.#ONE_YEAR = 360 * this.#ONE_DAY;
 
        // this.#MOON_CYCLE_PHASE = 12 * this.#VTIME_WEEK;
    }
 
    /******************************** MOON PHASES **********************************
    0% NM  7% WXC    40% FQM  57% WXG  90% FM  93% WNG  60% LQM  43% WNC  10% NM
    2% NM  10% WXC    43% FQM  60% WXG  93% FM  90% WNG  57% LQM  40% WNC  7% NM
    5% NM  12% WXC    45% FQM  62% WXG  95% FM  88% WNG  55% LQM  38% WNC  5% NM
            14% WXC    48% FQM  64% WXG  98% FM  86% WNG  52% LQM  36% WNC  2% NM
            17% WXC    50% FQM  67% WXG  100% FM  83% WNG  50% LQM  33% WNC
            19% WXC    52% FQM  69% WXG  98% FM  81% WNG  48% LQM  31% WNC
            21% WXC    55% FQM  71% WXG  95% FM  79% WNG  45% LQM  29% WNC
            24% WXC              74% WXG          76% WNG          26% WNC
            26% WXC              76% WXG          74% WNG          24% WNC
            29% WXC              79% WXG          71% WNG          21% WNC
            31% WXC              81% WXG          69% WNG          19% WNC
            33% WXC              83% WXG          67% WNG          17% WNC
            36% WXC              86% WXG          64% WNG          14% WNC
            38% WXC              88% WXG          62% WNG          12% WNC
    ********************************************************************************/
    /**   
    * Private function - supports moon phase calculations
    * @param vanatime (required) - Vanadiel time, in MILLISECONDS
    * @returns - integer - represents the day in the 84 day moon phase cycle
    */
    #moonDays(vanatime){
        if (vanatime === undefined || vanatime == null) vanatime = this.now();
        return  ((( vanatime /  this.#VTIME_DAY ) + 26) % this.#MOON_CYCLE_DAYS);  
    } 
 
    /**
    * Private function for doing arithmetic for moon phase percentage
    * @param vanatime (required) - Vanadiel time, in MILLISECONDS
    * @returns - integer representing moon phase percentage
    */
    #moonPercent(vanatime){  
        if (vanatime === undefined || vanatime == null) vanatime = this.now();
        return Math.abs( -Math.round((42 - Math.floor(this.#moonDays(vanatime))) / 42 * 100) );
    }
 
    /**
    * Helper function for getting moon phase percentage
    * @param vanatime (required) - Vanadiel time, in MILLISECONDS
    * @returns - integer representing moon phase percentage
    */
    getMoonPercent(vanatime) { return this.#moonPercent(vanatime)};
 
    /**
    * @param vanatime - Vanadiel time, in MILLISECONDS; default value is now()
    * @returns - integer representing waxing/waning/neither
    */
    #moonDirection(vanatime){
        if (vanatime === undefined || vanatime == null) vanatime = this.now();
        var moondays = Math.floor(this.#moonDays(vanatime));
        //console.log(daysmod);
        if (moondays == 42 || moondays == 0) { return 0; }// neither waxing nor waning
        else if (moondays < 42){ return 1; } // waning
        else{ return 2; } // waxing
    }


vYear = Math.floor(vanaDate / (360 * msRealDay));
    /**
vMon  = Math.floor((vanaDate % (360 * msRealDay)) / (30 * msRealDay)) + 1;
    * @param vanatime - Vanadiel time, in MILLISECONDS; default value is now()
vDate = Math.floor((vanaDate % (30 * msRealDay)) / (msRealDay)) + 1;
    * @returns - total milliseconds remaining until next conquest update
vHour = Math.floor((vanaDate % (msRealDay)) / (60 * 60 * 1000));
    */  
vMin  = Math.floor((vanaDate % (60 * 60 * 1000)) / (60 * 1000));
    moonLatentPhase(vanatime){
vSec  = Math.floor((vanaDate % (60 * 1000)) / 1000);
        if (vanatime === undefined || vanatime == null) vanatime = this.now();
vDay  = Math.floor((vanaDate % (8 * msRealDay)) / (msRealDay));
        var moonPhase = this.#moonPercent(vanatime);
        var moonDirection = this.#moonDirection(vanatime);


if (vYear < 1000) { VanaYear = "0" + vYear; } else { VanaYear = vYear; }
        //console.log("*mP", moonPhase);
if (vMon  < 10)   { VanaMon  = "0" + vMon; } else { VanaMon  = vMon; }
        //console.log("*mD", moonDirection);
if (vDate < 10)   { VanaDate = "0" + vDate; } else { VanaDate = vDate; }
       
if (vHour < 10)   { VanaHour = "0" + vHour; } else { VanaHour = vHour; }
        if (moonPhase <= 5 || (moonPhase <= 10 && moonDirection == 1)) {return 0;} // New Moon - 10% waning -> 5% waxing
if (vMin  < 10)   { VanaMin  = "0" + vMin; }  else { VanaMin  = vMin; }
        else if (moonPhase >= 7 && moonPhase <= 38 && moonDirection == 2) {return 1;} // Waxing Crescent - 7% -> 38% waxing
if (vSec  < 10)   { VanaSec  = "0" + vSec; }  else { VanaSec  = vSec; }
        else if (moonPhase >= 40 && moonPhase <= 55 && moonDirection == 2){return 2;} // First Quarter - 40%% -> 55% waxing
        else if (moonPhase >= 57 && moonPhase <= 88 && moonDirection == 2){return 3;} // Waxing Gibbous - 57% -> 88%
// Moon phase
        else if (moonPhase >= 95 || (moonPhase >= 90 && moonDirection == 2)){return 4;} // Full Moon - waxing 90% -> waning 95%
var timenow = new Date();
        else if (moonPhase >= 62 && moonPhase <= 93 && moonDirection == 1){return 5;}  // Waning Gibbous - 93% -> 62%
     var localTime = timenow.getTime();
        else if (moonPhase >= 45 && moonPhase <= 60 && moonDirection == 1){return 6;}  // Last Quarter - 60% -> 45%
    var moonDays = (Math.floor((localTime - Mndate.getTime()) / msGameDay))  % 84;
        else{return 7;} // Waning Crescent - 43% -> 12%
     }


     var mnElapsedTime = (localTime - Mndate.getTime()) % msGameDay;
     /**
    * @returns - total milliseconds remaining until next conquest update
    */
    conquestRemainingTime(){
        var now = new Date();
        //console.log(this.earthDate.getTime(), this.#VTIME_BIRTH);
        return (7 * (24 * 60 * 60 * 1000)) - ((now.getTime() - this.#VTIME_BIRTH) % (7 * (24 * 60 * 60 * 1000)));
    }


     // determine phase percentage
     /**
     moonpercent = - Math.round((42 - moonDays) / 42 * 100);
     * @returns - integer - total Vanadiel days remaining on current conquest
    if (moonpercent <= -94) {
    */  
         mnPhase = 0;
    conquestRemainingVanaDays(){
        optPhase = 4;
         return Math.ceil(this.conquestRemainingTime() / (24 * 60 * 60 * 1000 / 25));
        toNextPhase = (3 - moonDays) * msGameDay - mnElapsedTime;
    }
        toOptimalPhase = (38 - moonDays) * msGameDay - mnElapsedTime;


     } else if (moonpercent >= 90) {
    /**
    mnPhase = 0;
    * @param time - Vanadiel time, in MILLISECONDS, needing to be converted
         optPhase = 4;
     * @returns {Date} - value as Date() object
         toNextPhase = (87 - moonDays) * msGameDay - mnElapsedTime;
    */
         toOptimalPhase = (38 + 84 - moonDays) * msGameDay - mnElapsedTime;
    earthTime(time){
        if (time === undefined) time = this.now_inEarthMS();
         //else time = time * 60 * 1000;
         var earthTime = time / ( this.#VMULTIPLIER );
         return new Date(Math.floor(earthTime) - (this.#vanaBirthday - this.#VTIME_BIRTH));
    }


    }  else if (moonpercent >= -93 && moonpercent <= -62)  {
      mnPhase = 1;
        optPhase = 4;
        toNextPhase = (17 - moonDays) * msGameDay - mnElapsedTime;
        toOptimalPhase = (38 - moonDays) * msGameDay - mnElapsedTime;


     }  else if (moonpercent >= -61 && moonpercent <= -41{
     /**
      mnPhase = 2;
    * @param time - Vanadiel time, in minutes, needing to be converted
         optPhase = 4;
    * @returns - string, formatted 00:00
         toNextPhase = (25 - moonDays) * msGameDay - mnElapsedTime;
    */
         toOptimalPhase = (38 - moonDays) * msGameDay - mnElapsedTime;
    stringVanaTime(time){
        if (time === undefined || time === null) time = this.now();
        //console.log(time);
        var vYear = this.year(time);
        var vMon = this.month(time)
        var vDate = this.date(time);
         var vHour = this.hour(time);
         var vMin  = this.mins(time);
        // var vSec  = time.getSeconds();
         // var vDay  = time.getDay();
        //seconds left our because we don't use them for any calcs


    } else if (moonpercent >= -40 && moonpercent <= -11{
        if (vYear < 1000) vYear = "0" + vYear;
      mnPhase = 3;
        if (vMon  < 10) vMon = "0" + vMon;
         optPhase = 4;
         if (vDate  < 10)  vDate  = "0" + vDate;
         toNextPhase = (38 - moonDays) * msGameDay - mnElapsedTime;
         if (vHour < 10) vHour = "0" + vHour;  
         toOptimalPhase = (38 - moonDays) * msGameDay - mnElapsedTime;
         if (vMin  < 10) vMin  = "0" + vMin;  


     } else if (moonpercent >= -10 && moonpercent <= 6{
        //return vYear + ":" + vMonth + ":" + vDate + " [" + vHour + ":" + vMin + "]" ; // for testing
      mnPhase = 4;
        return vHour + ":" + vMin;
         optPhase = 0;
    }
         toNextPhase = (45 - moonDays) * msGameDay - mnElapsedTime;
   
         toOptimalPhase = (80 - moonDays) * msGameDay - mnElapsedTime;
    /**
    * @param time - Vanadiel time, in milliseconds, needing to be converted
     * @returns - string, formatted 00:00:00
    */
    stringEarthTime(time) {
        var eTime = this.earthTime(time);  
        // const monthNames = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
        //Most of these arent needed, left in for future inclusion if needed/wanted
        // var eYear = eTime.getFullYear();
        // var eMon  = monthNames[eTime.getMonth()];
        var eDate = eTime.getDate();
        var eHour = eTime.getHours();
        var eMin  = eTime.getMinutes();
        var eSec = eTime.getSeconds();
        var eDay  = eTime.getDay();
          
        // Assigns a leading zero if neccessary
        // if (eDate < 10)  eDate = "0" + eDate;  
         if (eHour < 10)  eHour = "0" + eHour;
        if (eMin < 10)   eMin  = "0" + eMin;
         if (eSec < 10)   eSec  = "0" + eSec;


    }  else if (moonpercent >= 7 && moonpercent <= 36)  {
        // var str = eMon + " " + eDate + "," + eHour + ":" + eMin + ":" + eSec;
      mnPhase = 5;
         var str = eHour + ":" + eMin + ":" + eSec;
         optPhase = 0;
         return str;
         toNextPhase = (58 - moonDays) * msGameDay - mnElapsedTime;
    }
        toOptimalPhase = (80 - moonDays) * msGameDay - mnElapsedTime;


     } else if (moonpercent >= 37 && moonpercent <= 56) {
    /**
      mnPhase = 6;
     * @param nextTime - Vana time, in MINUTES, needing to be converted
         optPhase = 0;
    * @returns - string of HOURS:MINS
        toNextPhase = (66 - moonDays) * msGameDay - mnElapsedTime;
    */
         toOptimalPhase = (80 - moonDays) * msGameDay - mnElapsedTime;
    timeUntil(nextTime){
        if (nextTime === undefined) { return "00:00:00"; }
         else if ( !(nextTime instanceof Date) ) {
            //console.log('!instance of Date');
            var e = this.earthTime(nextTime);
        } //earth time for next departure
         else e = nextTime;


    }  else if (moonpercent >= 57 && moonpercent <= 89)  {
        //console.log( e.getTime() );
      mnPhase = 7;
        optPhase = 0;
        toNextPhase = (60 - moonDays) * msGameDay - mnElapsedTime;
        toOptimalPhase = (80 - moonDays) * msGameDay - mnElapsedTime;
    }


    mnpercent = PhaseName[mnPhase]  + Math.abs(moonpercent) + "%";
        var now = new Date(); // earth time now
       
        //get difference between the next departure and current time... we want this to be in seconds, and Date()
        //and nextDeparture should be in milliseconds, so we /1000


    if (moonpercent <= 5 && moonpercent >= -10) {
        var days = (e.getTime() - now.getTime()) / (24 * 60 * 60 * 1000);
          mnpercent = "<FONT COLOR='#FF0000'>" + mnpercent+ "</FONT>";
        var hours = (days - Math.floor(days)) * 24;
    } else if (moonpercent >= 90 || moonpercent <= -95) {
        var minutes = (hours - Math.floor(hours)) * 60;
          mnpercent = "<FONT COLOR='#0000FF'>" + mnpercent+ "</FONT>";
        var seconds = Math.floor((minutes - Math.floor(minutes)) * 60);
    }


  //nextPhase = "Next phase (" + PhaseName[(mnPhase + 1) % 8] + "): " + formatCountdown(toNextPhase);
        days = Math.floor(days);
  //nextOptPhase = "Next " + PhaseName[optPhase] + ": " + formatCountdown(toOptimalPhase);
        hours = Math.floor(hours);
        minutes = Math.floor(minutes);
//draw to sidebar element
       
VanaTime = "<div><li><b>" + VanaHour + ":" + VanaMin + "</b>  ~ " + VanaYear + "-" + VanaMon + "-" + VanaDate + "</li>";  
        if (hours < 10) hours = "0" + hours;  
VanaTime += "<li><span style=\"font-weight: bold; font-size:14px; color:" + DayColor[vDay] + "\" >" + VanaDay[vDay] + "</span><span style=\"font-size:14px;\">  ~ " + mnpercent + "</span></li>";  
        if (minutes < 10)  minutes  = "0" + minutes;  
VanaTime += "</div>";
        if (seconds < 10)  seconds  = "0" + seconds;
       
alchemy_hours = guildHelper(8, 23, 6);
        var str = minutes + ":" + seconds;
bonecraft_hours = guildHelper(8, 23, 3);
clothcraft_hours = guildHelper(6, 21, 0);
cooking_hours = guildHelper(5, 20, 7);
fishing_hours = guildHelper(3, 18, 5);
goldsmithing_hours = guildHelper(8, 23, 4);
leathercraft_hours = guildHelper(3, 18, 4);
smithing_hours = guildHelper(8, 23, 2);
woodworking_hours = guildHelper(6, 21, 0);


        if ( days > 0 ) { str = [days, hours, str].join(':'); }
        else if ( hours > 0 || typeof(hours) == 'string') { str = [hours, str].join(':'); }


        return str;
    }


    timer(time1, time2){
        var seconds = Math.floor((time1.getTime() - time2.getTime()) / 1000);
               
        //basic math functions to get each element of time separated
        var minutes = Math.floor(seconds / 60);
        var hours = Math.floor(minutes / 60);
        var days = Math.floor(hours / 24);


// Do not alter this line. This updates sidebar text for current time
        hours = hours-(days * 24);
document.getElementById("n-vanatime").innerHTML = VanaTime;
        minutes = minutes-(days * 24 * 60)-(hours * 60);
        seconds = seconds-(days * 24 * 60 * 60 )-(hours * 60 * 60)-(minutes * 60);
const page_vanatime = document.getElementsByClassName("page-vanatime");
for (let i = 0; i < page_vanatime.length; i = i+1) { page_vanatime[i].textContent = VanaTime; }


const page_alchemy_hours = document.getElementsByClassName("page-alchemy_hours");
        seconds < 10 ? seconds = "0" + seconds : seconds;
for (let i = 0; i < page_alchemy_hours.length; i = i+1) { page_alchemy_hours[i].textContent = alchemy_hours; }
        minutes < 10 ? minutes = "0" + minutes : minutes;
        hours < 10 ? hours = "0" + hours : hours;
       
        var str = hours + ":" + minutes + ":" + seconds;
        if ( days != 0) str = [days, str].join(':');
        return str;  
    }


const page_bonecraft_hours = document.getElementsByClassName("page-bonecraft_hours");
}
for (let i = 0; i < page_bonecraft_hours.length; i = i+1) { page_bonecraft_hours[i].textContent = bonecraft_hours; }
const vanatime = new VanaTime();
let currentPopulation = 0;
const page_clothcraft_hours = document.getElementsByClassName("page-clothcraft_hours");
////////////////////////////////////////////////////
for (let i = 0; i < page_clothcraft_hours.length; i = i+1) { page_clothcraft_hours[i].textContent = clothcraft_hours; }


const page_cooking_hours = document.getElementsByClassName("page-cooking_hours");
function pageHasElement(element){
for (let i = 0; i < page_cooking_hours.length; i = i+1) { page_cooking_hours[i].textContent = cooking_hours; }
const page_fishing_hours = document.getElementsByClassName("page-fishing_hours");
    function test(v){
for (let i = 0; i < page_fishing_hours.length; i = i+1) { page_fishing_hours[i].textContent = fishing_hours; }
        var i = document.getElementById(v);
        var c = document.getElementsByClassName(v);
const page_goldsmithing_hours = document.getElementsByClassName("page-goldsmithing_hours");
for (let i = 0; i < page_goldsmithing_hours.length; i = i+1) { page_goldsmithing_hours[i].textContent = goldsmithing_hours; }
const page_leathercraft_hours = document.getElementsByClassName("page-leathercraft_hours");
for (let i = 0; i < page_leathercraft_hours.length; i = i+1) { page_leathercraft_hours[i].textContent = leathercraft_hours; }


const page_smithing_hours = document.getElementsByClassName("page-smithing_hours");
        if ( i ) { return true; }
for (let i = 0; i < page_smithing_hours.length; i = i+1) { page_smithing_hours[i].textContent = smithing_hours; }
        if ( c[0] ) { return true; }
    }
const page_woodworking_hours = document.getElementsByClassName("page-woodworking_hours");
for (let i = 0; i < page_woodworking_hours.length; i = i+1) { page_woodworking_hours[i].textContent = woodworking_hours; }


    if (typeof(element) == 'string') return test(element);
    else {
        for (const [k,v] of Object.entries(element) ){
            if (test(v) == true) return true;
        }
    }
    return false;
}
}
function guildHelper(open, close, holiday)  {
  var timenow = new Date();
  var localTime = timenow.getTime();
  var elapsedTime = (localTime - basisDate.getTime()) % msGameDay;
  var dayStart = localTime - elapsedTime;


  // this conversion factor tells us time elapsed since the game day started in milliseconds
function updateSidebar() {
  var convFactor = 60 * 60 * 1000 / 25;  
    if (!pageHasElement(timeElements.sidebar)) return;
 
 
  vanaDate = ((898 * 360 + 30) * msRealDay) + (localTime - basisDate.getTime()) * 25;
    var hour = vanatime.hour();
  vDay  = Math.floor((vanaDate % (8 * msRealDay)) / (msRealDay));
    hour < 10 ? hour = ["0", hour].join('') : hour;
    var mins = vanatime.mins();
    mins < 10 ? mins = ["0", mins].join('') : mins;
 
    var sidebarTime = "<div><li><b>" + hour + ":" + mins + "</b>  ~  " + vanatime.year() + "-" + vanatime.month() + "-" + vanatime.date() + "</li>";
sidebarTime += "<li><span style=\"font-weight: bold; font-size:14px; color:" + vanatime.dayColor() + "\" >"  + vanatime.dayLabel() + "</span><span style=\"font-size:14px;\">  ~ " + vanatime.moonPhaseIcon() + vanatime.getMoonPercent() + "%</span></li>";
 
    //sidebarTime += "<li>\u{1F7E2} Online ";
    if ( currentPopulation != 0 ) sidebarTime += "<li><span style=\"font-size:9px;\">\u{1F7E2}</span> Online <b>" + currentPopulation + "</b></li>";
    else sidebarTime += "<li><span style=\"font-size:9px;\">\u{1F534}</span> Online <i>~Standby~</i></li>";


  open = open * convFactor;
    sidebarTime += "</div>";
  close = close * convFactor;
 
  openTime = open + dayStart;
  closeTime = close + dayStart;
  outputTxt = "";
  guildCountdown = 0;
 
  if (openTime >= localTime) {
      guildCountdown = openTime - localTime;
      outputTxt2 = "Currently Closed. Open tomorrow.";
      outputTxt1 = "Opens in: ";
  } else if ((openTime < localTime) && (closeTime > localTime)) {
      guildCountdown = closeTime - localTime;
      outputTxt2 = "Currently Open for business.";
      outputTxt1 = "Closes in: ";
  } else if (closeTime <= localTime)  {
      guildCountdown = (msGameDay - elapsedTime) + open;
      outputTxt2 = "Currently Closed. Open tomorrow.";
      outputTxt1 = "Opens in: ";     
  }
 
  // adjust for holiday
  if ((holiday == vDay) && (closeTime > localTime)) {
      guildCountdown = (msGameDay - elapsedTime) + open;
      outputTxt2 = "Currently Closed for Holiday.";
      outputTxt1 = "Opens in: ";
  } else if (((vDay + 1) == holiday) && (closeTime <= localTime))  {
      guildCountdown = (msGameDay - elapsedTime) + open + msGameDay;
      outputTxt2 = "Currently Closed. Holiday tomorrow.";
      outputTxt1 = "Opens in: ";
  }


  return outputTxt1 + formatCountdown(guildCountdown) + ". " + outputTxt2;
const sidebar_vanatime = document.getElementById(timeElements.sidebar);
    if (sidebar_vanatime) sidebar_vanatime.innerHTML = sidebarTime;
//for (let i = 0; i < page_vanatime.length; i = i+1) { page_vanatime[i].innerHTML = sidebarTime; }
}
}


function formatCountdown(varTime) {
function updateBoatSchedule() {


  var dayLeft = varTime / msRealDay;
    if (!pageHasElement(timeElements.boats)) return;
  var hourLeft = (dayLeft - Math.floor(dayLeft)) * 24;
    populateTransportSchedule(timeElements.boats.selbina, schedule.boats.selbina, 3);
  var minLeft = (hourLeft - Math.floor(hourLeft)) * 60;
    populateTransportSchedule(timeElements.boats.mhaura, schedule.boats.mhaura, 3);
  var secLeft = Math.floor((minLeft - Math.floor(minLeft)) * 60);
    populateTransportSchedule(timeElements.boats.whitegate, schedule.boats.whitegate, 1);
  var formattedTime = '';
    populateTransportSchedule(timeElements.boats.nashmau, schedule.boats.nashmau, 1);
    populateTransportSchedule(timeElements.boats.bibiki, schedule.boats.bibiki, 1);
    populateTransportSchedule(timeElements.boats.purgonorgoIsle, schedule.boats.purgonorgoIsle, 1);
    populateTransportSchedule(timeElements.boats.northLanding, schedule.boats.northLanding, 1);
    populateTransportSchedule(timeElements.boats.centralLanding, schedule.boats.centralLanding, 1);
    populateTransportSchedule(timeElements.boats.southLanding, schedule.boats.southLanding, 1);
}


  dayLeft = Math.floor(dayLeft);
function updateAirshipSchedule(){
  hourLeft = Math.floor(hourLeft);
    if (!pageHasElement(timeElements.airships)) return;
  minLeft = Math.floor(minLeft);


   if (minLeft < 10) {minLeft = '0' + minLeft;}
   populateTransportSchedule(timeElements.airships.jeuno, schedule.airships.jeuno, 1);
   if (secLeft < 10) {secLeft = '0' + secLeft;}
  populateTransportSchedule(timeElements.airships.bastok, schedule.airships.bastok, 1);
  populateTransportSchedule(timeElements.airships.sandy, schedule.airships.sandy, 1);
  populateTransportSchedule(timeElements.airships.windy, schedule.airships.windy, 1);
   populateTransportSchedule(timeElements.airships.kazham, schedule.airships.kazham, 1);
}
           


  if (dayLeft > 0) {
      formattedTime = dayLeft + ':';
      if (hourLeft < 10) {
        formattedTime = formattedTime + '0' + hourLeft + ':';
      } else {
        formattedTime = formattedTime + hourLeft + ':';
      }       
  } else if (hourLeft > 0) {
      formattedTime = hourLeft + ':';
  }


  formattedTime = formattedTime + minLeft + ':' + secLeft;
function _transportScheduleHeader(classname){
  return formattedTime;
 
    var header = "<TABLE CLASS='" + classname + " vanatime-main-table" + "' CELLSPACING='0' CELLPADDING='0'>";
header += "<TR><TH ALIGN='LEFT'>Destination</TH>";
//header += "<TH ALIGN='LEFT'>Departure Day</TH>";
header += "<TH ALIGN='LEFT'>VanaTime</TH>";
    header += "<TH ALIGN='LEFT'>Earth Time</TH>";
header += "<TH ALIGN='LEFT'>Departs</TH></TR>";
return header;
}
}


function getAirSched() {  
function _transportScheduleBody(schedule, entriesPerSchedule, numberOfEntries){
    function _helper(sched, n){
        var helperoffset = (sched[1] + sched[3] + sched[4]) * 60  * 1000 / 25;  // VANA MILLISECONDS
        //console.log("helperoffset: ", helperoffset, vanatime.now_inMS());
        while (helperoffset <  vanatime.now_inMS() ) {
            helperoffset += (sched[2] * n  * 60 * 1000 / 25);
        }
        return helperoffset; // VANA MILLISECONDS
    }


  var timenow = new Date();  
    var html = "";
  var localTime = timenow.getTime();  
    var offset = {};
  var elapsedTime = (localTime - basisDate.getTime()) % msGameDay;
    var rowhighlight = true;
  var dayStart = localTime - elapsedTime;
  vanaDate = ((898 * 360 + 30) * msRealDay) + (localTime - basisDate.getTime()) * 25;
  vDay = Math.floor((vanaDate % (8 * msRealDay)) / (msRealDay));  


  var offset1 = ((1 * 60) + 10) * 60 * 1000 / 25; // 1:10 offset used by B->J J->S
    for( let n=1; n <= numberOfEntries; n++ ){
  var offset2 = ((2 * 60) + 40) * 60 * 1000 / 25; // 2:40 offset used by J->W K-J
        html += '<TR class="vanatime-main-table-row-spacer"><TD></TD><TD></TD><TD></TD><TD></TD></TR>';
  var offset3 = ((4 * 60) + 10) * 60 * 1000 / 25; // 4:10 offset used by J->B S->J
        for( let x=0; x < schedule.length; x++ ){
  var offset4 = ((5 * 60) + 35) * 60 * 1000 / 25; // 5:35 offset used by J->K
            const offsetVariable = x + (schedule.length * (n-1));
  var offset5 = ((5 * 60) + 45) * 60 * 1000 / 25; // 5:45 offset used by W->J


  outAir = "";
            for( let i=1 ; i <= entriesPerSchedule; i++ ){
  outAir += "<TABLE CLASS='vanatime-page-airship-table' WIDTH='400' CELLSPACING='0' CELLPADDING='0'>";
                console.log(n,x,i, offsetVariable);
  outAir += "<TR><TH ALIGN='LEFT'>Airship Route</TH>";
  outAir += "<TH ALIGN='LEFT'>Departure Day</TH>";
  outAir += "<TH ALIGN='LEFT'>Arrival</TH>";
  outAir += "<TH ALIGN='LEFT'>Departure</TH></TR>";
 
  outAir += AirHelper(elapsedTime, offset3, vDay, "Jeuno To Bastok");
  outAir += AirHelper(elapsedTime, offset4, vDay, "Jeuno To Kazham");
  outAir += AirHelper(elapsedTime, offset1, vDay, "Jeuno To San d'Oria");  
  outAir += AirHelper(elapsedTime, offset2, vDay, "Jeuno To Windurst");
  outAir += AirHelper(elapsedTime, offset1, vDay, "Bastok To Jeuno");
  outAir += AirHelper(elapsedTime, offset2, vDay, "Kazham To Jeuno");
  outAir += AirHelper(elapsedTime, offset3, vDay, "San d'Oria To Jeuno");
  outAir += AirHelper(elapsedTime, offset5, vDay, "Windurst To Jeuno");
 
  outAir += "</TABLE>";
  // when ready, delete this
  const airship_time = document.getElementById(airship_schedule);
  if (airship_time) airship_time.innerHTML = outAir;
 
}


function AirHelper(elapsed, offset, day, city) {
                if ( i > 1 ) offset[offsetVariable] +=  (schedule[x][2] * 60 * 1000 / 25);
                //else if ( lastOffset ) offset = lastOffset;
                else offset[offsetVariable] = _helper(schedule[x], n);


  var newOffset = offset;
                // offset must be greater than the offset before it
  var count = 0;
                if ( offsetVariable >= schedule.length && offset[offsetVariable] <= offset[offsetVariable - schedule.length] )
  while (newOffset < elapsed) {  
                    {
      count += 1;  
                        console.log( "fired " + offsetVariable );
      newOffset += (msGameDay / 4);  
                        offset[offsetVariable] = offset[offsetVariable - schedule.length] + (schedule[x][2] * 60 * 1000 / 25);
  }  
                    }
  if (count >= 4) {
 
      day = (day + 1) % 8;  
                var earthdepTime = (vanatime.today_inMS() + (offset[offsetVariable] * 25));
  }
                var vanadepTime = earthdepTime  / ( 60 * 1000);
 
                //var arrTime = depTime - ( sched[2][4] * 60 * 1000); /// LEAVE THIS in the event someone wants to add ARRIVAL TIMES to future HTML tables
 
 
  output = "<TR><TD>" + city + "</TD>";  
                //html += '<TR><TD>x= ' + x + '</TD><TD>' + vanatime.stringVanaTime(vanadepTime) + '</TD><TD>' + vanatime.stringEarthTime(earthdepTime) + '</TD><TD>' + vanatime.timeUntil(earthdepTime)  +'</TD></TR>';
  output += "<TD><FONT COLOR=" + DayColor[day] + ">" + VanaDay[day] + "</FONT></TD>";  
                var rowclass = "";
  arrivalTime = newOffset - elapsed - 144000;
                if ( rowhighlight ) rowclass = `class="vanatime-main-table-row-highlight"`;
  if (arrivalTime < 0) {
 
      arrivalTime = 0;
                html += '<TR ' + rowclass + '><TD>' + schedule[x][0] + '</TD><TD>' + vanatime.stringVanaTime(vanadepTime) + '</TD><TD>' + vanatime.stringEarthTime(earthdepTime) + '</TD><TD>' + vanatime.timeUntil(earthdepTime)  +'</TD></TR>';
  }
                rowhighlight = !rowhighlight;
  output += "<TD>" + formatCountdown(arrivalTime,1) + "</TD>";
            }
  output += "<TD>" + formatCountdown(newOffset - elapsed,1) + "</TD></TR>";  
        }
 
    }
  return output;  
    return html;
}
 
function populateTransportSchedule(classname, schedule, entriesPerSchedule){
    const shipSched = document.getElementById(classname);
if (shipSched) {
        var numberOfEntries = getSelectedNumberOfEntries(classname);
        if (numberOfEntries === 'undefined' || numberOfEntries === null || numberOfEntries == 0 ) numberOfEntries = 1;
 
        var _HTMLheader = _transportScheduleHeader(classname);
        var _HTMLbody = this._transportScheduleBody(schedule, entriesPerSchedule, numberOfEntries);
        var updatedSched =  _HTMLheader + _HTMLbody + "</TABLE>";
 
        const classname_details = classname + "-details";
        let t = shipSched.getElementsByClassName(classname_details)[0];
        var _HTMLdetails = '';
        if ( !t ){
 
            var temp = expandTableSelection(classname);
            if ( !temp ) temp = "";
            const div = '<div class="vanatime-main-table">' + temp ;
            _HTMLdetails = `<span class="${classname_details}">${updatedSched}</span>`;
            shipSched.innerHTML =  div + _HTMLdetails + '</div>';
 
        }
        else {
            t.innerHTML = updatedSched;
        }
       
    }
}
}


function getConquest()  {
function updateGuilds(){
    if (!pageHasElement(timeElements.guilds)) return;
 
    /* ALCHEMY */
const page_alchemy_hours = document.getElementById(timeElements.guilds.alchemy);
    if ( page_alchemy_hours ) { page_alchemy_hours.innerHTML = _guildSchedule(schedule.guilds.alchemy);  }
 
    const page_alchemy_status = document.getElementById(timeElements.guilds.alchemy_status);
    if ( page_alchemy_status ) { page_alchemy_status.innerHTML = _guildSchedule(schedule.guilds.alchemy, timeElements.guilds.alchemy_status);  }
 
    const page_alchemy_holiday = document.getElementById(timeElements.guilds.alchemy_holiday);
    if ( page_alchemy_holiday ) { page_alchemy_holiday.innerHTML = _guildSchedule(schedule.guilds.alchemy, timeElements.guilds.alchemy_holiday);  }
 
    const page_alchemy_timer = document.getElementById(timeElements.guilds.alchemy_timer);
    if ( page_alchemy_timer ) { page_alchemy_timer.innerHTML = _guildSchedule(schedule.guilds.alchemy, timeElements.guilds.alchemy_timer);  }
 
 
    /* BONECRAFT */
const page_bonecraft_hours = document.getElementById(timeElements.guilds.bonecrafting);
if ( page_bonecraft_hours ) { page_bonecraft_hours.innerHTML = _guildSchedule(schedule.guilds.bonecrafting);  }
 
    const page_bonecraft_status = document.getElementById(timeElements.guilds.bonecraft_status);
    if ( page_bonecraft_status ) { page_bonecraft_status.innerHTML = _guildSchedule(schedule.guilds.bonecrafting, timeElements.guilds.bonecraft_status);  }
 
    const page_bonecraft_holiday = document.getElementById(timeElements.guilds.bonecraft_holiday);
    if ( page_bonecraft_holiday ) { page_bonecraft_holiday.innerHTML = _guildSchedule(schedule.guilds.bonecrafting, timeElements.guilds.bonecraft_holiday);  }
 
    const page_bonecraft_timer = document.getElementById(timeElements.guilds.bonecraft_timer);
    if ( page_bonecraft_timer ) { page_bonecraft_timer.innerHTML = _guildSchedule(schedule.guilds.bonecrafting, timeElements.guilds.bonecraft_timer); }
 
 
    /* CLOTHCRAFT */
const page_clothcraft_hours = document.getElementById(timeElements.guilds.clothcrafting);
if ( page_clothcraft_hours ) { page_clothcraft_hours.innerHTML = _guildSchedule(schedule.guilds.clothcrafting);  }
 
    const page_clothcraft_status = document.getElementById(timeElements.guilds.clothcraft_status);
    if ( page_clothcraft_status ) { page_clothcraft_status.innerHTML = _guildSchedule(schedule.guilds.clothcrafting, timeElements.guilds.clothcraft_status);  }
 
    const page_clothcraft_holiday = document.getElementById(timeElements.guilds.clothcraft_holiday);
    if ( page_clothcraft_holiday ) { page_clothcraft_holiday.innerHTML = _guildSchedule(schedule.guilds.clothcrafting, timeElements.guilds.clothcraft_holiday);  }
 
    const page_clothcraft_timer = document.getElementById(timeElements.guilds.clothcraft_timer);
    if ( page_clothcraft_timer ) { page_clothcraft_timer.innerHTML = _guildSchedule(schedule.guilds.clothcrafting, timeElements.guilds.clothcraft_timer);  }
 
 
    /* COOKING */
const page_cooking_hours = document.getElementById(timeElements.guilds.cooking);
if ( page_cooking_hours ) { page_cooking_hours.innerHTML = _guildSchedule(schedule.guilds.cooking);  }
 
    const page_cooking_status = document.getElementById(timeElements.guilds.cooking_status);
    if ( page_cooking_status ) { page_cooking_status.innerHTML = _guildSchedule(schedule.guilds.cooking, timeElements.guilds.cooking_status);  }
 
    const page_cooking_holiday = document.getElementById(timeElements.guilds.cooking_holiday);
    if ( page_cooking_holiday ) { page_cooking_holiday.innerHTML = _guildSchedule(schedule.guilds.cooking, timeElements.guilds.cooking_holiday);  }
 
    const page_cooking_timer = document.getElementById(timeElements.guilds.cooking_timer);
    if ( page_cooking_timer ) { page_cooking_timer.innerHTML = _guildSchedule(schedule.guilds.cooking, timeElements.guilds.cooking_timer);  }
 
    /* FISHING */
const page_fishing_hours = document.getElementById(timeElements.guilds.fishing);
if ( page_fishing_hours ) { page_fishing_hours.innerHTML = _guildSchedule(schedule.guilds.fishing);  }
 
    const page_fishing_status = document.getElementById(timeElements.guilds.fishing_status);
    if ( page_fishing_status ) { page_fishing_status.innerHTML = _guildSchedule(schedule.guilds.fishing, timeElements.guilds.fishing_status);  }
 
    const page_fishing_holiday = document.getElementById(timeElements.guilds.fishing_holiday);
    if ( page_fishing_holiday ) { page_fishing_holiday.innerHTML = _guildSchedule(schedule.guilds.fishing, timeElements.guilds.fishing_holiday);  }
 
    const page_fishing_timer = document.getElementById(timeElements.guilds.fishing_timer);
    if ( page_fishing_timer ) { page_fishing_timer.innerHTML = _guildSchedule(schedule.guilds.fishing, timeElements.guilds.fishing_timer);  }
 
    /* GOLDSMITHING */
const page_goldsmithing_hours = document.getElementById(timeElements.guilds.goldsmithing);
if ( page_goldsmithing_hours ) {  page_goldsmithing_hours.innerHTML = _guildSchedule(schedule.guilds.goldsmithing);  }
 
    const page_goldsmithing_status = document.getElementById(timeElements.guilds.goldsmithing_status);
    if ( page_goldsmithing_status ) { page_goldsmithing_status.innerHTML = _guildSchedule(schedule.guilds.goldsmithing, timeElements.guilds.goldsmithing_status);  }
 
    const page_goldsmithing_holiday = document.getElementById(timeElements.guilds.goldsmithing_holiday);
    if ( page_goldsmithing_holiday ) { page_goldsmithing_holiday.innerHTML = _guildSchedule(schedule.guilds.goldsmithing, timeElements.guilds.goldsmithing_holiday);  }
 
    const page_goldsmithing_timer = document.getElementById(timeElements.guilds.goldsmithing_timer);
    if ( page_goldsmithing_timer ) { page_goldsmithing_timer.innerHTML = _guildSchedule(schedule.guilds.goldsmithing, timeElements.guilds.goldsmithing_timer);  }
 
    /* LEATHERCRAFT */
const page_leathercraft_hours = document.getElementById(timeElements.guilds.leathercrafting);
if ( page_leathercraft_hours ) { page_leathercraft_hours.innerHTML = _guildSchedule(schedule.guilds.leathercrafting);  }
 
    const page_leathercraft_status = document.getElementById(timeElements.guilds.leathercraft_status);
    if ( page_leathercraft_status ) { page_leathercraft_status.innerHTML = _guildSchedule(schedule.guilds.leathercrafting, timeElements.guilds.leathercraft_status);  }
 
    const page_leathercraft_holiday = document.getElementById(timeElements.guilds.leathercraft_holiday);
    if ( page_leathercraft_holiday ) { page_leathercraft_holiday.innerHTML = _guildSchedule(schedule.guilds.leathercrafting, timeElements.guilds.leathercraft_holiday);  }
 
    const page_leathercraft_timer = document.getElementById(timeElements.guilds.leathercraft_timer);
    if ( page_leathercraft_timer ) { page_leathercraft_timer.innerHTML = _guildSchedule(schedule.guilds.leathercrafting, timeElements.guilds.leathercraft_timer);  }
 
 
    /* SMITHING */
const page_smithing_hours = document.getElementById(timeElements.guilds.smithing);
if ( page_smithing_hours ) { page_smithing_hours.innerHTML = _guildSchedule(schedule.guilds.smithing); }
 
    const page_smithing_status = document.getElementById(timeElements.guilds.smithing_status);
    if ( page_smithing_status ) { page_smithing_status.innerHTML = _guildSchedule(schedule.guilds.smithing, timeElements.guilds.smithing_status);  }
 
    const page_smithing_holiday = document.getElementById(timeElements.guilds.smithing_holiday);
    if ( page_smithing_holiday ) { page_smithing_holiday.innerHTML = _guildSchedule(schedule.guilds.smithing, timeElements.guilds.smithing_holiday);  }
 
    const page_smithing_timer = document.getElementById(timeElements.guilds.smithing_timer);
    if ( page_smithing_timer ) { page_smithing_timer.innerHTML = _guildSchedule(schedule.guilds.smithing, timeElements.guilds.smithing_timer);  }


  var now = new Date();
    /* WOODWORKING */
  var timeLeft = (7 * msRealDay) - ((now.getTime() - basisDate.getTime()) % (7 * msRealDay));
const page_woodworking_hours = document.getElementById(timeElements.guilds.woodworking);
  vanaConq = Math.floor(timeLeft / (msGameDay)) + 1;
if ( page_woodworking_hours ) { page_woodworking_hours.innerHTML = _guildSchedule(schedule.guilds.woodworking); }


  conqDays = timeLeft / msRealDay;
    const page_woodworking_status = document.getElementById(timeElements.guilds.woodworking_status);
  conqHours = (conqDays - Math.floor(conqDays)) * 24;
    if ( page_woodworking_status ) { page_woodworking_status.innerHTML = _guildSchedule(schedule.guilds.woodworking, timeElements.guilds.woodworking_status); }
  conqMin = (conqHours - Math.floor(conqHours)) * 60;
  conqSec = Math.floor((conqMin - Math.floor(conqMin)) * 60);


  if (conqDays < 10) { conqDays = '0' + Math.floor(conqDays); } else { conqDays = Math.floor(conqDays); }
    const page_woodworking_holiday = document.getElementById(timeElements.guilds.woodworking_holiday);
  if (conqHours < 10) { conqHours = '0' + Math.floor(conqHours ); } else { conqHours = Math.floor(conqHours ); }
    if ( page_woodworking_holiday ) { page_woodworking_holiday.innerHTML = _guildSchedule(schedule.guilds.woodworking, timeElements.guilds.woodworking_holiday); }
  if (conqMin < 10) { conqMin = '0' + Math.floor(conqMin ); } else { conqMin = Math.floor(conqMin ); }
  if (conqSec < 10) { conqSec = '0' + Math.floor(conqSec ); } else { conqSec = Math.floor(conqSec ); }
 
  conqDate = formatDate(now.getTime() + timeLeft, 2);


  conq = vanaConq + ' Vana´diel Days <BR>' + conqDate + ' (' + formatCountdown(timeLeft) + ')';
    const page_woodworking_timer = document.getElementById(timeElements.guilds.woodworking_timer);
    if ( page_woodworking_timer ) { page_woodworking_timer.innerHTML = _guildSchedule(schedule.guilds.woodworking, timeElements.guilds.woodworking_timer); }


  const conquest_time = document.getElementById(conquest_schedule);
    const page_allguilds_hours = document.getElementById(timeElements.guilds.all);
   if (conquest_time) conquest_time.innerHTML = conq;  
if ( page_allguilds_hours ) { 
       
        var guildOut = "<TABLE CLASS='"+ timeElements.guilds.all + " vanatime-main-table" + "' CELLSPACING='0' CELLPADDING='0' border='1px solid black'>";
        guildOut = guildOut + "<TR><TH ALIGN='center' WIDTH=100 >Guild</TH>";
        guildOut = guildOut + "<TH ALIGN='center' WIDTH=175 >Status</TH></TR>";
        guildOut = guildOut + "<TR><TH> Alchemy</TH><td>" +        _guildSchedule(schedule.guilds.alchemy)        + "</td></TR>";
        guildOut = guildOut + "<TR><TH> Blacksmithing</TH><td>" +  _guildSchedule(schedule.guilds.smithing)     + "</td></TR>";
        guildOut = guildOut + "<TR><TH> bonecrafting</TH><td>" +    _guildSchedule(schedule.guilds.bonecrafting) + "</td></TR>";
        guildOut = guildOut + "<TR><TH> Goldsmithing</TH><td>" +   _guildSchedule(schedule.guilds.goldsmithing) + "</td></TR>";
        guildOut = guildOut + "<TR><TH> Clothcrafting</TH><td>" +  _guildSchedule(schedule.guilds.clothcrafting) + "</td></TR>";
        guildOut = guildOut + "<TR><TH> Woodworking</TH><td>" +    _guildSchedule(schedule.guilds.woodworking) + "</td></TR>";
        guildOut = guildOut + "<TR><TH> Leatherworking </TH><td>" + _guildSchedule(schedule.guilds.leathercrafting) + "</td></TR>";
        guildOut = guildOut + "<TR><TH> Fishing</TH><td>" +        _guildSchedule(schedule.guilds.fishing)     + "</td></TR>";
        guildOut = guildOut + "<TR><TH> Cooking</TH><td>" +        _guildSchedule(schedule.guilds.cooking)     + "</td></TR>";
        guildOut = guildOut + "</TABLE>";
   
        page_allguilds_hours.innerHTML = guildOut;  
    }
}
}


function _guildStatusCheck(guild, statusID) {


function getMoonPhase()  {
    return outputText = [outputTxt1, outputTxt2];
}


  var timenow = new Date();
function _guildSchedule(guild, statusID) {
  var localTime = timenow.getTime();
   
  var moonDays = (Math.floor((localTime - Mndate.getTime()) / msGameDay))  % 84;
    if (guild === 'undefined') return "_guildSchedule: error";
   
    var now = vanatime.now_inMS();
    var guildOpens = guild[0] * 60 * 1000 / 25;
    var guildCloses = guild[1] * 60 * 1000 / 25;


  var mnElapsedTime = (localTime - Mndate.getTime()) % msGameDay;
    // Guild open/close check
    var nextOpenTime = 0;
    var outputTxt1 = "", outputTxt2 = "";
    if (guildOpens >= now) {
        nextOpenTime = (((guildOpens - now) * 25) + vanatime.now_inEarthMS());
        outputTxt1 = "Opens in: ";
        outputTxt2 = "Currently Closed. Open tomorrow.";
    } else if ((guildOpens < now) && (guildCloses > now)) {
        nextOpenTime = (((guildCloses - now) * 25) + vanatime.now_inEarthMS());
        outputTxt1 = "Closes in: ";
        outputTxt2 = "Currently Open for business.";
    } else if (guildCloses <= now)  {
        nextOpenTime = ((24 * 60 * 60 * 1000 / 25) - now  + guildOpens) * 25 + vanatime.now_inEarthMS();
        outputTxt1 = "Opens in: ";
        outputTxt2 = "Currently Closed. Open tomorrow.";
    }


  // determine phase percentage
    if(typeof(statusID) == 'string' && statusID.includes('status')) {
        moonpercent = - Math.round((42 - moonDays) / 42 * 100);
        // outputTxt2 = [`<span id=\'${statusID}\'>`, outputTxt2].join('');  
        if (moonpercent <= -94) {
        // outputTxt2 = [outputTxt2, '</span'].join('');
            mnPhase = 0;
        return outputTxt2;
            optPhase = 4;
    }
            toNextPhase = (3 - moonDays) * msGameDay - mnElapsedTime;
   
            toOptimalPhase = (38 - moonDays) * msGameDay - mnElapsedTime;


        }  else if (moonpercent >= 90)  {
    // Holiday check
    mnPhase = 0;
    if ((guild[2] == vanatime.weekDay()) && (guildCloses > now)) {
             optPhase = 4;
        nextOpenTime = ((24 * 60 * 60 * 1000 / 25- now + guildOpens) * 25 + vanatime.now_inEarthMS();
             toNextPhase = (87 - moonDays) * msGameDay - mnElapsedTime;
        outputTxt2 = "Currently Closed for Guild Holiday.";
             toOptimalPhase = (38 + 84 - moonDays) * msGameDay - mnElapsedTime;
        outputTxt1 = "Opens in: ";
        if(typeof(statusID) == 'string' &&statusID.includes('holiday')) {
            // outputTxt2 = [`<span id=\'${statusID}\'>`, outputTxt2].join('');  
             // outputTxt2 = [outputTxt2, '</span'].join('');
             return outputTxt2;
        }
    } else if (((vanatime.weekDay() + 1) == guild[2]) && (guildCloses <= now))  {
        nextOpenTime = ((24 * 60 * 60 * 1000 / 25)  - now + guildOpens) * 25 + (24 * 60 * 60 * 1000 / 25) + vanatime.now_inEarthMS();
        outputTxt2 = "Currently Closed. Guild Holiday tomorrow.";
        outputTxt1 = "Opens in: ";
        if(typeof(statusID) == 'string' && statusID.includes('holiday')) {
            // outputTxt2 = [`<span id=\'${statusID}\'>`, outputTxt2].join('');  
             // outputTxt2 = [outputTxt2, '</span'].join('');
            return outputTxt2;
        }
    }


        }  else if (moonpercent >= -93 && moonpercent <= -62) {
    if(typeof(statusID) == 'string' &&statusID.includes('holiday')) {
      mnPhase = 1;
        // outputTxt2 = [`<span id=\'${statusID}\'>`, outputTxt2].join('');  
            optPhase = 4;
        // outputTxt2 = [outputTxt2, '</span'].join('');
            toNextPhase = (17 - moonDays) * msGameDay - mnElapsedTime;
        return `Guild is not on holiday until ${vanatime.dayLabel(guild[2])}.`;
            toOptimalPhase = (38 - moonDays) * msGameDay - mnElapsedTime;
    }


        }  else if (moonpercent >= -61 && moonpercent <= -41) {
    /****DEBUGGING*****/
      mnPhase = 2;
    // if (guild == schedule.guilds.fishing) {
            optPhase = 4;
    //    console.log(guild[2], nextOpenTime, now, vanatime.timeUntil(nextOpenTime) );
            toNextPhase = (25 - moonDays) * msGameDay - mnElapsedTime;
    // }
            toOptimalPhase = (38 - moonDays) * msGameDay - mnElapsedTime;
    /****DEBUGGING*****/


        }  else if (moonpercent >= -40 && moonpercent <= -11) {
    if(typeof(statusID) == 'string' && statusID.includes('timer')) {
      mnPhase = 3;
        return outputTxt1 + vanatime.timeUntil(nextOpenTime);
            optPhase = 4;
    }
            toNextPhase = (38 - moonDays) * msGameDay - mnElapsedTime;
    return outputTxt1 + vanatime.timeUntil(nextOpenTime) + ". " + outputTxt2;
            toOptimalPhase = (38 - moonDays) * msGameDay - mnElapsedTime;


        } else if (moonpercent >= -10 && moonpercent <= 6)  {
}
      mnPhase = 4;
            optPhase = 0;
            toNextPhase = (45 - moonDays) * msGameDay - mnElapsedTime;
            toOptimalPhase = (80 - moonDays) * msGameDay - mnElapsedTime;


        } else if (moonpercent >= 7 && moonpercent <= 36) {
function updateConquest() {
      mnPhase = 5;
    if (!pageHasElement(timeElements.conquest)) return;
            optPhase = 0;
   
            toNextPhase = (58 - moonDays) * msGameDay - mnElapsedTime;
    function stringNextConquest(remaining){
            toOptimalPhase = (80 - moonDays) * msGameDay - mnElapsedTime;
        //if (nextConquest === undefined) { return "stringConquestTimer: timer undefined"; }
        const now = new Date();
        remaining = now.getTime() + remaining;
 
        var nextConquest = new Date(remaining);
        var tempHour = nextConquest.getHours();
        var tempMin  = nextConquest.getMinutes();
        var tempSec  = nextConquest.getSeconds();
 
        if (tempHour < 10)  tempHour = "0" + tempHour;
        if (tempMin < 10)   tempMin  = "0" + tempMin;
        if (tempSec < 10)  tempSec  = "0" + tempSec; 


        }  else if (moonpercent >= 37 && moonpercent <= 56)  {
        var strNextConquest = nextConquest.toDateString() + " " + tempHour + ":" + tempMin + ":" + tempSec;
      mnPhase = 6;
            optPhase = 0;
            toNextPhase = (66 - moonDays) * msGameDay - mnElapsedTime;
            toOptimalPhase = (80 - moonDays) * msGameDay - mnElapsedTime;


        }  else if (moonpercent >= 57 && moonpercent <= 89) {
            // var remaining = (temp.getTime() - now.getTime()) / (24 * 60 * 60 * 1000);
      mnPhase = 7;
             // var hours = (remaining - Math.floor(remaining)) * 24;
             optPhase = 0;
             // var mins = (hours - Math.floor(hours)) * 60;
             toNextPhase = (60 - moonDays) * msGameDay - mnElapsedTime;
             // var secs = Math.floor((mins - Math.floor(mins)) * 60);
             toOptimalPhase = (80 - moonDays) * msGameDay - mnElapsedTime;
        }


        mnpercent = PhaseName[mnPhase] + " (" + Math.abs(moonpercent) + "%)";
            // remaining = Math.floor(remaining);
            // hours = Math.floor(hours);
            // mins = Math.floor(mins);


        if (moonpercent <= 5 && moonpercent >= -10)  {
            // if (hours < 10)  hours = "0" + hours;
              mnpercent = "<FONT COLOR='#FF0000'>" + mnpercent+ "</FONT>";
            // if (mins < 10)  mins  = "0" + mins;
        } else if (moonpercent >= 90 || moonpercent <= -95{
            // if (secs < 10)   secs = "0" + secs;
              mnpercent = "<FONT COLOR='#0000FF'>" + mnpercent+ "</FONT>";
        }


  nextPhase = "Next phase (" + PhaseName[(mnPhase + 1) % 8] + "): " + formatCountdown(toNextPhase);
            // // tempDays < 10 ? ["0", tempDays].join(':') : tempDays;
  nextOptPhase = "Next " + PhaseName[optPhase] + ": " + formatCountdown(toOptimalPhase);
            // // tempDays > 0 ? [tempDays, strTimer].join(':') : null;


  //mnpercent = "<DIV onmouseover='javascript:getMoonDetails()'>" + mnpercent + "</DIV>  ";
            // strDiff = hours + ":" + mins + ":" + secs;
//document.getElementById("mPhase").innerHTML = mnpercent + nextPhase + "<BR>" + nextOptPhase;
const moon_phase_div = document.getElementById(moon_phase);
if (moon_phase_div) moon_phase_div.innerHTML = mnpercent + nextPhase + "<BR>" + nextOptPhase;;


        //return strNextConquest + "(" + strDiff + ")";
        return strNextConquest + "(" + vanatime.timeUntil(nextConquest) + ")";
    }
   
    const now = new Date();
    var remaining = vanatime.conquestRemainingTime();
    remainingVanaDaysOnConquest = vanatime.conquestRemainingVanaDays();
   
    conq = remainingVanaDaysOnConquest + ' Vana´diel Days <BR>';
    conq += stringNextConquest(remaining); //+ ' (' + timer.getHours() + ":" + timer.getMinutes() + ":" + timer.getSeconds() + ')';


  // new moon starts on day 38 (-10%)
    const conquest_time = document.getElementById(timeElements.conquest);
  // full moon starts at 80 (90%)
    if (conquest_time) conquest_time.innerHTML = conq;
  // Moon cycle lasts 84 game days.
}


  // Determine most recent full moon
  var fullMoonBasis = Mndate.getTime() + (3 * msGameDay);
  var repeatCal = 5;


  moonCal = "<TABLE CLASS='blank' CELLSPACING='0' CELLPADDING='0'><TR><TH WIDTH='115' ALIGN='left'>New Moon Start</TH><TH WIDTH='115' ALIGN='left'>New Moon End</TH><TH WIDTH='115' ALIGN='left'>Full Moon Start</TH><TH WIDTH='115' ALIGN='left'>Full Moon End</TH></TR>";
function updateMoonPhaseSchedule(){
  for ( i = 0; i < repeatCal; i++) {
    if (!pageHasElement(timeElements.moonSchedule)) return;
      elapsedCycles = Math.floor( (localTime - fullMoonBasis) / (84 * msGameDay) ) + i;
      FullEnd = fullMoonBasis + (elapsedCycles * 84 * msGameDay);
      //Full Moon starts 7 games days prior to end
      FullStart = FullEnd - 7 * msGameDay;
      //New Moon starts 49 games days prior to Full Moon end
      NewStart = FullEnd - 49 * msGameDay;
      //New Moon ends 42 games days prior to Full Moon end
      NewEnd = FullEnd - 42 * msGameDay;


      moonCal = moonCal + "<TR><TD>" + formatDate(NewStart, 2) + "</TD><TD>" + formatDate(NewEnd, 2) + "</TD><TD>" + formatDate(FullStart,2) + "</TD><TD>" + formatDate(FullEnd, 2) + "</TD></TR>";
    populateMoonPhaseSchedule(timeElements.moonSchedule, 7);
  }
  if (repeatCal < 1) { moonCal = ""; } else { moonCal = moonCal + '</TABLE>'; }
 
  // moon_phases
  //document.getElementById("mCalendar").innerHTML = moonCal;
  const moon_phases_schedule_div = document.getElementById(moon_phases_schedule);
if (moon_phases_schedule_div) moon_phases_schedule_div.innerHTML = moonCal;
}
}


function formatDate(varTime, showDay) {
function _moonPhaseScheduleHeader(classname){
    var header = "<TABLE CLASS='" + classname + " vanatime-main-table" + "' WIDTH='500' CELLSPACING='0' CELLPADDING='0'>";
header += "<TR ><TH ALIGN='LEFT'>Moon Phase</TH>";
header += "<TH ALIGN='LEFT'>Start Time</TH>";
header += "<TH ALIGN='LEFT'>End Time</TH>";
    //header += "<TH ALIGN='LEFT'>Phase Ends in...</TH>";
    header += "</TR>";
return header;
}
 
function _moonPhaseScheduleBody(numberOfEntries){
    var html = "";
   
    if ( numberOfEntries === undefined || numberOfEntries === null ) numberOfEntries = 7;
 
    // DEBUGGING
    numberOfEntries = 7;
 
        var vTempTime = vanatime.today_inMS() / (60 * 1000); // VANA TIME IN TOTAL MINUTES
        var thisMoonPhase = vanatime.moonLatentPhase(vTempTime);
 
        var lunarOffset = 0,  
            _time;
        for(var x = 0 ; x < numberOfEntries; x++ ){
 
            if ( x > 0 ){
                lunarOffset += 7;
                if ( thisMoonPhase == 1 || thisMoonPhase == 3 || thisMoonPhase == 5 || thisMoonPhase == 7) {
                    _time = vTempTime + (60 * 24 * lunarOffset);
                    if ( vanatime.moonLatentPhase(_time) == thisMoonPhase) lunarOffset += 7;
                }
                //console.log(thisMoonPhase);
 
                thisMoonPhase++;
                if (thisMoonPhase > 7) thisMoonPhase = 0;
                //console.log(thisMoonPhase);
            }


  var varDate = new Date(varTime);
            var phaseStartTime;
  var yyyy = varDate.getYear();
            for( var i = 1 ; i >= -13 ; i-- ) {
                _time = vTempTime + (60 * 24 * lunarOffset) + (60 * 24 * (i - 1)); // VANA MINUTES
            //console.log("s", thisMoonPhase, lunarOffset, vanatime.moonLatentPhase(_time));
               
                if ( vanatime.moonLatentPhase(_time) != thisMoonPhase ) {
                    //console.log( "start", i, vanatime.moonLatentPhase(_time), thisMoonPhase );
                    break;
                }
                else phaseStartTime = _time * 60; // VANA SECONDS
            }
           


  var mm = varDate.getMonth() + 1;
            var phaseEndTime;
  if (mm < 10) { mm = "0" + mm; }
            for( var i = -1 ; i <= 14 ; i++ ) {
                _time = vTempTime + (60 * 24 * lunarOffset) + (60 * 24 * (i + 1));// VANA MINUTES
            //console.log("e", thisMoonPhase,lunarOffset,vanatime.moonLatentPhase(_time));
           
                if ( vanatime.moonLatentPhase(_time) != thisMoonPhase ) {
                    //console.log( "end", i, vanatime.moonLatentPhase(_time), thisMoonPhase );
                    break;
                }
                else phaseEndTime = (_time + (60 * 24)) * 60; // VANA SECONDS
                // for loop breaks when _time is at 00:00 for the day the moon phase changes...
                // we add another day to this (60 + 24) to get the start of the next day for the table
            }  
           


  var dd = varDate.getDate();
            const startDate = new Date(Math.floor(phaseStartTime / (25 / 1000)) - vanatime.getDifference() );
  if (dd < 10) { dd = "0" + dd; }
            const endDate = new Date(Math.floor(phaseEndTime / (25 / 1000)) - vanatime.getDifference() );


  var day = varDate.getDay();
            var strDetails = vanatime.moonPhaseIcon(thisMoonPhase) + " " + vanatime.moonPhaseName(thisMoonPhase) + " "  + vanatime.moonPhasePercentages(thisMoonPhase);          


  var hh = varDate.getHours();
            function dateString(date){
 
                var sec = date.getSeconds(), hrs = date.getHours(), mins = date.getMinutes();  
  if (hh < 10) { hh = "0" + hh; }
                sec < 10 ? sec = "0" + sec : sec;
                hrs < 10 ? hrs = "0" + hrs : hrs;
                mins < 10 ? mins = "0" + mins : mins;
                return vanatime.weekdayLabel(date.getDay()) + ", " + date.getDate() + " " + vanatime.monthLabel(date.getMonth()) + " " + hrs + ":" + mins + ":" + sec;
            }


  var min = varDate.getMinutes();
            html += '<TR ><TD>' + strDetails + '</TD><TD>' + dateString(startDate) + '</TD><TD>' + dateString(endDate) + '</TD>';
  if (min < 10) { min = "0" + min; }


  var ss = varDate.getSeconds();
            // const now = new Date();
  if (ss < 10) { ss = "0" + ss; }
            // var strTimer = vanatime.timer(endDate, now);
            // html += '<TD>' + strTimer + '</TD>';


  if (showDay == 1)  {
            html += '</TR>';
      dateString = EarthDay[day] + ", " + sMonth[mm-1] + ' ' + dd + ', ' + yyyy + " " + hh + ":" + min + ":" + ss;
        }
  } else if (showDay == 2)  {
    return html;
      dateString = sMonth[mm-1] + " " + dd + ",  " + hh + ":" + min + ":" + ss;
  }
  return dateString;
}
}


function getShipSched() {
function populateMoonPhaseSchedule(classname, numberOfEntries){
   
   
    const moonSchedule = document.getElementById(classname);
    if (moonSchedule) {
        numberOfEntries = Number(moonSchedule.getAttribute('data-entries'));
        if (numberOfEntries === undefined || numberOfEntries === null || numberOfEntries == 0 ) numberOfEntries = 7;
        else numberOfEntries = numberOfEntries * 7;
        var _HTMLheader = this._moonPhaseScheduleHeader(classname);
        var _HTMLbody = this._moonPhaseScheduleBody(numberOfEntries);
        moonSchedule.innerHTML = _HTMLheader + _HTMLbody + "</TABLE>";
    }
}


  // Boats depart at 00:00, 08:00, 16:00 from both Selbina and Mhuara
  // Boats arrive at 06:30, 14:30, 22:30 which is 216 seconds before they leave
  var now = new Date();
  var timeDiff = now.getTime() - basisDate.getTime();
  var hours = Math.floor((timeDiff / ( msGameDay / 3)) % 3);


  var timeLeft = (msGameDay / 3 ) - (timeDiff % (msGameDay / 3));
function updateRSE()  {
    const rseclass = timeElements.rseSchedule;
    const rseSched = document.getElementById(rseclass);
if (!rseSched) return;
   
        function formatDate(varTime, showDay) {


  var repeatFerry 5 ;//document.Timer.FerryCount.value;
            var varDate = new Date(varTime);
            var yyyy = varDate.getYear();
       
            var mm = varDate.getMonth() + 1;
            if (mm < 10) { mm = "0" + mm; }
       
            var dd = varDate.getDate();
            if (dd < 10) { dd = "0" + dd; }
       
            var day = varDate.getDay();
       
            var hh = varDate.getHours();
           
            if (hh < 10) { hh = "0" + hh; }
       
            var min = varDate.getMinutes();
            if (min < 10) { min = "0" + min; }
       
            var ss = varDate.getSeconds();
            if (ss < 10) { ss = "0" + ss; }
            if (showDay == 1) {
            dateString = vanatime.weekdayLabel(day) + ", " + vanatime.monthLabel(mm-1) + ' ' + dd + ', ' + yyyy + " " + hh + ":" + min + ":" + ss;
            } else if (showDay == 2)  {
            dateString = vanatime.monthLabel(mm-1) + " " + dd + ",  " + hh + ":" + min + ":" + ss;
            }
            return dateString;
        }
   
    var timenow = new Date();
    var localtime = timenow.getTime();


  bSched = "<TABLE CLASS='blank' CELLSPACING='0' CELLPADDING='0'><TR><TH WIDTH='80' ALIGN='left'>Arrives</TH>";
    // ???
  bSched = bSched + "<TH WIDTH='45'></TH><TH ALIGN='left' WIDTH='80'>ETA</TH>";
    var repeatCal = 8;
  bSched = bSched + "<TH WIDTH='80' ALIGN='left'>Departs</TH><TH WIDTH='45'></TH>";
   
  bSched = bSched + "<TH ALIGN='left' WIDTH='45'>ETD</TH></TR>";
    var rowclass;
    var rowhighlight = true;


  vanaDate = ((898 * 360 + 30) * msRealDay) + (timeDiff) * 25;
    RSECal = "<TABLE CLASS=\'" + rseclass + " vanatime-main-table" + "\' WIDTH='500' CELLSPACING='0' CELLPADDING='0'><TR><TH ALIGN='LEFT'>Date & Time</TH><TH ALIGN='LEFT'>Race</TH><TH ALIGN='LEFT'>Location</TH></TR>"
  vDay  = Math.floor((vanaDate % (8 * msRealDay)) / (msRealDay));
   
    var numberOfEntries = getSelectedNumberOfEntries(rseclass);
    if (numberOfEntries === 'undefined' || numberOfEntries === null || numberOfEntries == 0 ) numberOfEntries = 1;


  for ( i = 0; i < repeatFerry; i++) {
    numberOfEntries = 1;
      timeLeftLoop = timeLeft + ( i* msGameDay / 3);
      dPos = (vDay + Math.floor((hours + 1 + i)/3)) % 8;
      dPos2 = (dPos + BoatDayOffset[(hours + i) % 3]) % 8;
      arrivalTime = timeLeftLoop - 216000;
      if (arrivalTime <= 0)
        arrivalTime = 0;


      bSched = bSched + '<TR><TD><FONT COLOR="' + DayColor[dPos2] + '">' + VanaDay[dPos2] + '</FONT></TD><TD>' + BoatSched2[(hours + i) % 3] + '</TD><TD>' + formatCountdown(arrivalTime) + '</TD><TD><FONT COLOR="' + DayColor[dPos] + '">' + VanaDay[dPos] + '</FONT></TD><TD>' + BoatSched[(hours + i) % 3] + '</TD><TD ALIGN="left">' + formatCountdown(timeLeftLoop) + '</TD></TR>';
    for( let n=1; n <= numberOfEntries; n++ ){
  }
       
      for ( i = 0; i < repeatCal; i++) {
          elapsedWeeks = Math.floor( (localtime - vanatime.rseDate) / (8 * (24 * 60 * 60 * 1000 / 25)) ) + i;
          RSEstart = vanatime.rseDate + (elapsedWeeks * 8 * (24 * 60 * 60 * 1000 / 25));
         
          if ( rowhighlight ) rowclass = `class="vanatime-main-table-row-highlight"`;
          else rowclass = "";


  if (repeatFerry < 1) { out = ''; } else { out = bSched + "</TABLE>"; }
          RSECal = RSECal + "<TR " + rowclass + "><TD>" + formatDate(RSEstart,2) + '</TD><TD>' + vanatime.rseRace(elapsedWeeks % 8) + '</TD><TD>';
  //document.getElementById("ferry").innerHTML = out;
          //RSECal = RSECal + "<A HREF=#  onmousedown='javascript:getRSEDetails(" + (elapsedWeeks % 3) + ")'>";
          RSECal = RSECal + vanatime.rseLocation(elapsedWeeks % 3);
          RSECal = RSECal + '</A></TD></TR>';
          rowhighlight = !rowhighlight;
      }
    //  if (repeatCal < 1) { RSECal = ""; }  
    //  else { RSECal = RSECal + '</TABLE>'; }
    RSECal += '<TR class="vanatime-main-table-row-spacer"><TD></TD><TD></TD><TD></TD></TR>';


const ferry_schedule_div = document.getElementById(ferry_schedule);
    }
if (ferry_schedule_div) ferry_schedule_div.innerHTML = out;
    RSECal = RSECal + '</TABLE>';
    // else {
    //    RSECal = "<TABLE CLASS='blank' CELLPADDING='0' CELLSPACING='0'><TR><TH WIDTH='120' ALIGN='LEFT'>Start</TH><TH WIDTH='120' ALIGN='LEFT'>End</TH><TH WIDTH='60' ALIGN='LEFT'>Location</TH></TR>"
    //    offsetTime = race * 8 * msGameDay;
    //    for ( i = 0; i < repeatCal; i++) {
    //      elapsedWeeks = Math.floor( (localtime - vanatime.rseDate) / (64 * msGameDay) ) + i;
         
    //      elapsedLocationWeeks = Math.floor( (localtime - vanatime.rseDate) / (8 * msGameDay) ) + (8 * i);
    //      raceOffset = race - (elapsedLocationWeeks % 8);
    //      elapsedLocationWeeks = elapsedLocationWeeks + raceOffset;
    //      RSEstart = vanatime.rseDate + (elapsedWeeks * 64 * msGameDay) + offsetTime ;
    //      RSEend = RSEstart + (8 * msGameDay);
    //      RSECal = RSECal + "<TR><TD>" + formatDate(RSEstart,2) + "</TD><TD>" + formatDate(RSEend,2) + "</TD><TD>";
    //      //RSECal = RSECal + "<A HREF=#  onmousedown='javascript:getRSEDetails(" + (elapsedLocationWeeks % 3) + ")'>";
    //      RSECal = RSECal + RSEloc[(elapsedLocationWeeks) % 3]
    //      RSECal = RSECal + "</A></TD></TR>";
    //    }
    //  if (repeatCal < 1) { RSECal = ""; } else { RSECal = RSECal + '</TABLE>'; }
    // }
 
    const classname_details = rseclass + "-details";
    let t = document.getElementsByClassName(classname_details)[0];
    var _HTMLdetails = '';
    //console.log(t);
    if ( t === 'undefined' || t === null || !t ){
       
        var temp = expandTableSelection(rseclass);
        if ( !temp ) temp = "";
        const div = '<div class="vanatime-main-table">' + temp ;
        _HTMLdetails = `<span class="${classname_details}">${RSECal}</span>`;
        rseSched.innerHTML = div + _HTMLdetails + '</div>';
 
    }
    else {
        console.log(t);
        t.innerHTML = RSECal;
    }
   
    //document.getElementById(rseclass).innerHTML = RSECal;
}
 
function expandTableSelection(classname){
 
    const classnameheader = classname + "-header";
    const classnameshowSelect = classname + "-showSelect";
 
    if ( classname == timeElements.rseSchedule ) {
        return `
        <div class="${classnameheader}" style="background:#FFFFFF00; width: max-content; float: right;">Shown:
        <select id="${classnameshowSelect}" style="background:#DFDFDF50; ">
            <option value="0">All </option>
            <option value="1">M. Hume</option>
            <option value="2">F. Hume</option>
            <option value="3">M. Elvaan</option>
            <option value="4">F. Elvaan</option>
            <option value="5">M. TaruTaru</option>
            <option value="6">F. TaruTaru</option>
            <option value="7">Mithra</option>
            <option value="8">Galka</option>
 
        </select>
        </div>
    `;
    }
 
    return `
        <div class="${classnameheader}" style="background:#FFFFFF00; width: max-content; float: right;">Shown:
        <select id="${classnameshowSelect}" style="background:#DFDFDF50; ">
            <option value="0">1 </option>
            <option value="1">3</option>
            <option value="2">5</option>
            <option value="3">10</option>
        </select>
        </div>
    `;
}
}


function getGuildHours() {
function expandRaceTable(classname){
    const classnameheader = classname + "-header";
    const classnameshowSelect = classname + "-showSelect";
 
    const temp = `
        <div class="${classnameheader}" style="background:#FFFFFF00; width: max-content; float: right;">Shown:
        <select id="${classnameshowSelect}" style="background:#DFDFDF50; ">
            <option value="0">1 </option>
            <option value="1">3</option>
            <option value="2">5</option>
            <option value="3">10</option>
        </select>
        </div>
    `;
      
      
  alchemy = guildHelper(8, 23, 6);
    // return popupButton;
  blacksmith = guildHelper(8, 23, 2);
    return temp;
  bonework = guildHelper(8, 23, 3);
}
  goldsmith = guildHelper(8, 23, 4);
  cloth = guildHelper(6, 21, 0);
  wood = guildHelper(6, 21, 0);
  leather = guildHelper(3, 18, 4);
  fishing = guildHelper(3, 18, 5);
  cooking = guildHelper(5, 20, 7);


 
function getSelectedNumberOfEntries(classname){
  guildOut = "<TABLE CLASS='blank' CELLSPACING='0' CELLPADDING='0'>";
    // Number of entries comes from selection dropdown
  guildOut = guildOut + "<TR><TH ALIGN='left' WIDTH=100>Guild</TH>";
    var e = document.getElementById(classname + "-showSelect");
  guildOut = guildOut + "<TH WIDTH=125></TH><TH WIDTH=125></TH><TH ALIGN='left' WIDTH=140>Status</TH></TR>";
    if ( !e ) return 1;
  guildOut = guildOut + "<TR><td> Alchemy</td>" + alchemy + "</TR>";
    var entries;
  guildOut = guildOut + "<TR> Blacksmith" + blacksmith + "</TR>";
    switch(e.value) {
  guildOut = guildOut + "<TR>" + bonework + "</TR>";
        case "1":
  guildOut = guildOut + "<TR>" + goldsmith + "</TR>";
            entries = 3;
  guildOut = guildOut + "<TR>" + cloth + "</TR>";
          break;
  guildOut = guildOut + "<TR>" + wood + "</TR>";
        case "2":
  guildOut = guildOut + "<TR><TD>" + "<A HREF=#  onmousedown='javascript:guildDetail(7)'>Leather</A>" + "</TD>" + leather + "</TR>";
            entries = 5;
  guildOut = guildOut + "<TR><TD>" + "<A HREF=#  onmousedown='javascript:guildDetail(8)'>Fishing</A>" + "</TD>" + fishing + "</TR>";
          break;
  guildOut = guildOut + "<TR><TD>" + "<A HREF=#  onmousedown='javascript:guildDetail(9)'>Cooking</A>" + "</TD>" + cooking + "</TR>";
        case "3":
  guildOut = guildOut + "</TABLE>";
            entries = 10;
 
          break;
//document.getElementById(crafting_guilds_schedule).innerHTML = guildOut;
        default:
const crafting_guilds_schedule_div = document.getElementById(crafting_guilds_schedule);
            entries = 1;
if (crafting_guilds_schedule_div) crafting_guilds_schedule_div.innerHTML = guildOut;
      }
    //console.log(classname + ":" + entries);
    return entries;
}
}


function gametick(){
function gametick(){
resetGameTime();
   
getConquest();
    updateSidebar();
getMoonPhase();
    updateAirshipSchedule();
getAirSched();
    updateBoatSchedule();
getShipSched();
    updateGuilds();
getGuildHours();
updateConquest();
setTimeout("gametick()", 50);
    updateMoonPhaseSchedule();
    updateRSE();
 
setTimeout("gametick()", 1000);
    //console.log('gametick');
}
}


gametick();
gametick();
async function fetchWithTimeout(resource, options = {}) {
    const { timeout = 10000 } = options;
    const controller = new AbortController();
    const id = setTimeout(() => controller.abort(), timeout);
    const response = await fetch(resource, {
      ...options,
      signal: controller.signal
    });
    clearTimeout(id);
    return response;
  }
async function populationFetch(){
    try {
        const response = await fetchWithTimeout('https://api.horizonxi.com/api/v1/misc/exp-sync-status', {});
        const fetchedPopulation = await response.json();
        //if ( typeof(fetchedPopulation) == 'number' ) currentPopulation = fetchedPopulation;
        //console.log(fetchedPopulation);
        //return games;
        //console.log('populationFetchTick', fetchedPopulation);
        //populationFetch();
        currentPopulation = fetchedPopulation;
        setTimeout("startPopulationFetch()", 300000);
      } catch (error) {
        //fetched = 0;
        console.log(error.name === 'AbortError');
        startPopulationFetch();
      }
      //console.log('currentPopulation = ', currentPopulation);
      //return fetched;
}
function startPopulationFetch(){
    //console.log('startPopulationFetch');
    populationFetch();
    // if ( currentPopulation == 0) startPopulationFetch();
    // else setTimeout("startPopulationFetch()", 7000);
}
startPopulationFetch();

Latest revision as of 14:54, 16 July 2024

/*********************************************************************
Javascript below contributes to Vana'diel Time displays throughout
the HorizonXI Wiki
Credits: ********
https://www.pyogenes.com/ffxi/timer/v2.html 
https://www.mithrapride.org/vana_time/index.html
https://www.rubydoc.info/gems/vanadiel-time/Vanadiel/Time
**********************************************************************/

class timeElements {
// List of all class names for each element in this model, for styling 

	static sidebar = "n-vanatime";
    static conquest = "vanatime-page-conquest-schedule"; // <span ... />
    //static moonPhase = "vanatime-page-moon-phase";   // not implemented
    static moonSchedule = "vanatime-page-moon-schedule-table";
    static rseSchedule = "vanatime-page-rse-schedule-table";
    
    static airships = {
        //all : "vanatime-airship-schedule-table",     // not implemented
        jeuno : "vanatime-airship-schedule-jeuno-table",
        bastok : "vanatime-airship-schedule-bastok-table",
        sandy : "vanatime-airship-schedule-sandy-table",
        windy : "vanatime-airship-schedule-windy-table",
        kazham : "vanatime-airship-schedule-kazham-table",
    };

    static boats = {
        selbina : "vanatime-boat-schedule-selbina-table",
        mhaura : "vanatime-boat-schedule-mhaura-table",
        bibiki : "vanatime-boat-schedule-bibiki-table",
        purgonorgoIsle : "vanatime-boat-schedule-purgonorgoIsle-table",
        northLanding : "vanatime-boat-schedule-northLanding-table",
        centralLanding : "vanatime-boat-schedule-centralLanding-table",
        southLanding : "vanatime-boat-schedule-southLanding-table"
    };

    static guilds = {
        alchemy : "vanatime-guild-schedule-alchemy",
        alchemy_status:  "vanatime-guild-schedule-alchemy-status",
        alchemy_holiday: "vanatime-guild-schedule-alchemy-holiday",
        alchemy_timer: "vanatime-guild-schedule-alchemy-timer",

        smithing : "vanatime-guild-schedule-smithing",
        smithing_status:  "vanatime-guild-schedule-smithing-status",
        smithing_holiday: "vanatime-guild-schedule-smithing-holiday",
        smithing_timer: "vanatime-guild-schedule-smithing-timer",

        bonecrafting : "vanatime-guild-schedule-bonecrafting",
        bonecrafting_status:  "vanatime-guild-schedule-bonecrafting-status",
        bonecrafting_holiday: "vanatime-guild-schedule-bonecrafting-holiday",
        bonecrafting_timer: "vanatime-guild-schedule-bonecrafting-timer",

        goldsmithing : "vanatime-guild-schedule-goldsmithing",
        goldsmithing_status:  "vanatime-guild-schedule-goldsmithing-status",
        goldsmithing_holiday: "vanatime-guild-schedule-goldsmithing-holiday",
        goldsmithing_timer: "vanatime-guild-schedule-goldsmithing-timer",

        clothcrafting : "vanatime-guild-schedule-clothcrafting",
        clothcrafting_status:  "vanatime-guild-schedule-clothcrafting-status",
        clothcrafting_holiday: "vanatime-guild-schedule-clothcrafting-holiday",
        clothcrafting_timer: "vanatime-guild-schedule-clothcrafting-timer",

        woodworking : "vanatime-guild-schedule-woodworking",
        woodworking_status:  "vanatime-guild-schedule-woodworking-status",
        woodworking_holiday: "vanatime-guild-schedule-woodworking-holiday",
        woodworking_timer: "vanatime-guild-schedule-woodworking-timer",

        leathercrafting : "vanatime-guild-schedule-leathercrafting",
        leathercrafting_status:  "vanatime-guild-schedule-leathercrafting-status",
        leathercrafting_holiday: "vanatime-guild-schedule-leathercrafting-holiday",
        leathercrafting_timer: "vanatime-guild-schedule-leathercrafting-timer",

        fishing : "vanatime-guild-schedule-fishing",
        fishing_status:  "vanatime-guild-schedule-fishing-status",
        fishing_holiday: "vanatime-guild-schedule-fishing-holiday",
        fishing_timer: "vanatime-guild-schedule-fishing-timer",

        cooking : "vanatime-guild-schedule-cooking",
        cooking_status:  "vanatime-guild-schedule-cooking-status",
        cooking_holiday: "vanatime-guild-schedule-cooking-holiday",
        cooking_timer: "vanatime-guild-schedule-cooking-timer",

        all : "vanatime-guild-schedule-all"
    };
}


class schedule {
    //  All raw datea is from ASB 
    //  route definintion = [ 'Name of route', anim_arrive, anim_depart, timeOffset, time_interval, time_anim_arrive, time_waiting, time_anim_depart]
    //  Actual arrival time (when the player can enter the transport ) = time_offset + time_anim_arrive = [3] + [5]
    //  Departure time = arrival time + time_waiting = arrival_time + [6]
    //  All values listed are in VanaTime minutes, so 1440 is the total minutes in a game hour
    
        static #airship_jeuno_sandy = ['Jeuno -> Sandoria',   0,   360, 12, 60, 12];
        static #airship_jeuno_windy = ['Jeuno -> Windurst',   90,  360, 12, 60, 12];
        static #airship_jeuno_bastok = ['Jeuno -> Bastok',     180, 360, 12, 60, 16];
        static #airship_jeuno_kazham = ['Jeuno -> Kazham',     270, 360, 20, 50, 20];
        static #airship_bastok_jeuno = ['Bastok -> Jeuno',     0,   360, 14, 60, 16];
        static #airship_sandy_jeuno = ['Sandoria -> Jeuno',   180, 360, 12, 60, 16];
        static #airship_windy_jeuno = ['Windurst -> Jeuno',   270, 360, 18, 60, 14];
        static #airship_kazham_jeuno = ['Kazham -> Jeuno',     90,  360, 20, 50, 20];
    
        static airships = {
            jeuno : [
                this.#airship_jeuno_bastok,
                this.#airship_jeuno_sandy,
                this.#airship_jeuno_windy,
                this.#airship_jeuno_kazham,
                this.#airship_bastok_jeuno,
                this.#airship_sandy_jeuno,
                this.#airship_windy_jeuno,
                this.#airship_kazham_jeuno
            ],
            bastok :    [ this.#airship_bastok_jeuno, this.#airship_jeuno_bastok],
            sandy :     [this.#airship_sandy_jeuno, this.#airship_jeuno_sandy],
            windy :     [this.#airship_windy_jeuno, this.#airship_jeuno_windy],
            kazham :    [this.#airship_kazham_jeuno, this.#airship_jeuno_kazham]
        }
    
        static #boat_selbina_mhaura = ['Selbina -> Mhaura',     382, 480, 18, 80, 17];
        static #boat_mhaura_selbina = ['Mhaura -> Selbina',     382, 480, 18, 80, 17];
        static #boat_mhaura_whitegate = ['Mhaura -> Whitegate', 142, 480, 18, 80, 17];
    
        static #boat_whitegate_mhaura = ['Whitegate -> Mhaura', 142, 480, 18, 80, 16];
        static #boat_whitegate_nashmau = ['Whitegate -> Nashmau', 282, 480, 18, 180, 17];
        static #boat_nashmau_whitegate = ['Nashmau -> Whitegate', 282, 480, 18, 180, 17];
    
        static #boat_bibiki_tours = ['Bibiki Bay -> Tours', 710, 720, 20, 40, 20];
        static #boat_bibiki_purgo = ['Bibiki Bay -> Purgonorgo Isle', 270, 720, 20, 40, 20];
        static #boat_purgo_bibiki = ['Purgonorgo Isle -> Bibiki Bay', 500, 720, 20, 40, 20];
    
        static #boat_barge_south_central_emfa = ['South Landing -> Central Landing EMFEA', 5, 1440, 15, 35, 15];
        static #boat_barge_central_south_newtpool1 = ['Central Landing -> South Landing NewtPool', 267, 1440, 12, 30, 15];
        static #boat_barge_south_oos = ['South Landing -> OOS', 1402, 1440, 33,  0,  0];
        static #boat_barge_south_north = ['South Landing -> North Landing', 560, 1440, 15, 35, 15];
        static #boat_barge_north_oos = ['North Landing -> OOS', 925, 1440, 40,  0,  0];
        static #boat_barge_north_central = ['North Landing -> Central Landing',  993, 1440, 12, 40, 15];
        static #boat_barge_central_south_newtpool2 = ['Central Landing -> South Landing NewtPool 2', 1148, 1440, 12, 30, 15];
        static #boat_barge_south_oos1 = ['South Landing -> OOS 2', 512, 1440, 33,  0,  0];
    
        static boats = {
            selbina :  [ this.#boat_selbina_mhaura ],
            mhaura :  [ this.#boat_mhaura_selbina, this.#boat_mhaura_whitegate ],
            whitegate : [ this.#boat_whitegate_mhaura, this.#boat_whitegate_nashmau],
            nashmau : [ this.#boat_nashmau_whitegate ],
            bibiki :  [ this.#boat_bibiki_tours, this.#boat_bibiki_purgo],
            purgonorgoIsle : [ this.#boat_purgo_bibiki ],
            northLanding : [ this.#boat_barge_north_oos, this.#boat_barge_north_central ],
            centralLanding : [ this.#boat_barge_central_south_newtpool1, this.#boat_barge_central_south_newtpool2 ],
            southLanding : [ this.#boat_barge_south_central_emfa, this.#boat_barge_south_oos, this.#boat_barge_south_north, this.#boat_barge_south_oos1 ]
        }
    
        static #alchemy = [480, 1380, 5];
        static #smithing = [480, 1380, 2];
        static #bonecrafting = [480, 1380, 3];
        static #goldsmithing = [480, 1380, 4];
        static #clothcrafting = [360, 1260, 0];
        static  #woodworking = [360, 1260, 0];
        static #leathercrafting = [180, 1080, 4];
        static #fishing = [180, 1080, 5];
        static #cooking = [300, 1200, 7];
    
        static guilds = {
            // [ Open, Close, Holiday]
            alchemy : this.#alchemy,
            smithing : this.#smithing,
            bonecrafting : this.#bonecrafting,
            goldsmithing : this.#goldsmithing,
            clothcrafting : this.#clothcrafting,
            woodworking : this.#woodworking,
            leathercrafting : this.#leathercrafting,
            fishing : this.#fishing,
            cooking : this.#cooking,
            all : [ this.#alchemy, this.#smithing, this.#bonecrafting,this.#goldsmithing, this.#clothcrafting,this.#woodworking,this.#leathercrafting, this.#fishing, this.#cooking ]
        }
    
}

class VanaTime{
    
    #elementalDay =     ["Firesday",    "Earthsday",        "Watersday",        "Windsday",         "Iceday",           "Lightningday",     "Lightsday",    "Darksday"];
    #dayColor =         ["#FF0000",     "#AAAA00",          "#0000DD",           "#00AA22",         "#7799FF",          "#AA00AA",          "#AAAAAA",      "#333333"];
    #moonPhaseName =    ["New Moon",    "Waxing Crescent",  "First Quarter",    "Waxing Gibbous",   "Full Moon",        "Waning Gibbous",   "Last Quarter", "Waning Crescent"];
    #moonIcon =         ['\u{1F311}',   '\u{1F312}',        '\u{1F313}',        '\u{1F314}',        '\u{1F315}',        '\u{1F316}',        '\u{1F317}',    '\u{1F318}'];
    #moonPercentages =  ["(10%-0%-5%)", "(7%-38%)",         "(40%-55%)",        "(57%-88%)",        "(90%-100%-95%)",    "(93%-62%)",       "(60%-45%)",    "(43%-12%)"];
    #month =            ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
    #weekday =          ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
    #rseRace =          ["M. Hume","F. Hume","M. Elvaan","F. Elvaan","M. TaruTaru","F. TaruTaru","Mithra","Galka"];
    #rseLocation =      ["Gusgen Mines","Shakrami Maze","Ordelle Caves"];

    
    // Conversions in Milliseconds
    //#baseDate; // built at runtime: 1024844400000
    #moonDate =     1074997872000; // in milliseconds
    #VTIME_BIRTH     = 1024844400000; // vana birthday - in milliseconds
    #VTIME_BASEDATE  = 1009810800;  // unix epoch - 1009810800 = se epoch (in earth seconds)
    rseDate =      1075281264000;

    #ONE_SECOND = 1000000;
    #ONE_MINUTE;
    #ONE_HOUR;
    #ONE_DAY;
    // #ONE_WEEK;
    // #ONE_MONTH;
    // #ONE_YEAR;

    // Conversions in Minutes
    #VTIME_YEAR  =      518400;   // 360 * GameDay
    #VTIME_MONTH =      43200;      // 30 * GameDay
    #VTIME_WEEK  =      11520;      // 8 * GameDay
    #VTIME_DAY   =      1440;       // 24 hours * GameHour
    #VTIME_HOUR  =      60;         // 60 minutes
    #VMULTIPLIER =      25;
    #MOON_CYCLE_DAYS =  84;
    // #MOON_CYCLE_PHASE = 17280;     // 1 moon phase cycle = 12 vana days = 12 * Gameday

    /*
        Some of the math...

        (((898 * 360) + 30) * 24 * 60 * 60) / (this.#VMULTIPLIER / 1000).....  
        converts vana time to earth time by ( / 25 ) then getting to milliseconds ( * 1000 )
    */
    #vanaBirthday = (((898 * 360) + 30) * 24 * 60 * 60) / (25 / 1000); // 1117359360000 - in earth time milliseconds
    #difference = this.#vanaBirthday - this.#VTIME_BIRTH; // 92514960000 - earth time milliseconds

    getDifference(){ return this.#difference };

    /**
     * Common variables for quick reference to current Vana time
     * @returns - integer / number
     */
    // vana_currentTime_inEarthMS = ((898 * 360 + 30) * (24 * 60 * 60 * 1000)) + (this.earthDate.getTime() - this.#VTIME_BIRTH) * this.#VMULTIPLIER; // vana time, represented in earth milliseconds - used for making Date() objects
    // vana_currentTime = this.vana_currentTime_inEarthMS / (60 * 1000); // vana time in minutes
    // //vana_currentTime = ( ((this.earthDate.getTime() / 1000) - this.#VTIME_BASEDATE ) / 60.0 * this.#VMULTIPLIER) + (886 * this.#VTIME_YEAR); //returned in vana minutes
    // vana_year = this.vana_currentTime / this.#VTIME_YEAR;
    // vana_month  = (this.vana_currentTime / this.#VTIME_MONTH) % 12 + 1;
    // vana_date = (this.vana_currentTime / this.#VTIME_DAY) % 30 + 1;
    // vana_weekday  = Math.floor((this.vana_currentTime % this.#VTIME_WEEK) / this.#VTIME_DAY);
    // vana_hour = (this.vana_currentTime % this.#VTIME_DAY) / this.#VTIME_HOUR;
    // vana_mins  = this.vana_currentTime % this.#VTIME_HOUR;
    // vana_moonphase  = Math.floor((this.vana_currentTime % this.#MOON_CYCLE_PHASE) / this.#VTIME_DAY);

    /**
     * Helper functions for quick reference to any Vana time
     * @returns - integer / number
     */
    now_inEarthMS(){ 
        var now = new Date();
        return ((898 * 360 + 30) * (24 * 60 * 60 * 1000)) + (now.getTime() - this.#VTIME_BIRTH) * this.#VMULTIPLIER; 
    }

    now(){ return this.now_inEarthMS() / ( 60 * 1000); }

    now_inMS(){
        var timenow = new Date();
        return ((timenow.getTime() - this.#VTIME_BIRTH) % (24 * 60 * 60 * 1000 / 25));
    }

    now_inMINS(){
        return this.now_inMS / ( 1000 * 60 );
    }

    today_inMS(){ // result in earth Milliseconds
        var now = this.now_inEarthMS();
        return ( now - ( now % (24 * 60 * 60 * 1000) ));
    }

    year(vanatime) {
        if (vanatime === undefined || vanatime == null) vanatime = this.now();
        return Math.floor(vanatime / this.#VTIME_YEAR);
    }

    month(vanatime){
        if (vanatime === undefined || vanatime == null) vanatime = this.now();
        return Math.floor((vanatime / this.#VTIME_MONTH) % 12) + 1;
    }

    date(vanatime){
        if (vanatime === undefined || vanatime == null) vanatime = this.now();
        return Math.floor((vanatime / this.#VTIME_DAY) % 30) + 1;
    }
    
    weekDay(vanatime){
        if (vanatime === undefined || vanatime == null) vanatime = this.now();
        return Math.floor((vanatime % this.#VTIME_WEEK) / this.#VTIME_DAY);
    }

    hour(vanatime){
        if (vanatime === undefined || vanatime == null) vanatime = this.now();
        return Math.floor((vanatime % this.#VTIME_DAY) / this.#VTIME_HOUR);
    }

    mins(vanatime){
        if (vanatime === undefined || vanatime == null) vanatime = this.now();
        return Math.floor(vanatime % this.#VTIME_HOUR);
    }

    dayColor(vanatime){ 
        if (vanatime === undefined || vanatime == null) vanatime = this.now();
        return this.#dayColor[this.weekDay()];
    }
    
    dayLabel(day){ 
        if (day === undefined || day == null) return this.#elementalDay[this.weekDay()];
        else return this.#elementalDay[day]
    }

    moonPhaseIcon(day){  
        if (day === undefined || day == null) return this.#moonIcon[this.moonLatentPhase()];
        else return this.#moonIcon[day];
    }

    moonPhaseName(day){  
        if (day === undefined || day == null) return this.#moonPhaseName[this.moonLatentPhase()];
        else return this.#moonPhaseName[day];
    }

    moonPhasePercentages(day){  
        if (day === undefined || day == null) return this.#moonPercentages[this.moonLatentPhase()];
        else return this.#moonPercentages[day];
    }

    /**     
     * Time Helper functions - supports string generation for month/week details
     * @param  (required) - represents which RACE 
     * @returns - string - from #rseRace or #rseLocation
     */
    monthLabel(m){ return this.#month[m]; }
    weekdayLabel(w){ return this.#weekday[w]; }

    /**     
     * RSE Helper functions - supports string generation for RSE details
     * @param r (required) - represents which RACE 
     * @returns - string - from #rseRace or #rseLocation
     */ 
    rseRace(r){ return this.#rseRace[r]; }

    /**     
     * RSE Helper functions - supports string generation for RSE details
     * @param r (required) - represents which RACE 
     * @returns - string - from #rseRace or #rseLocation
     */ 
    rseLocation(r){ return this.#rseLocation[r]; }

    constructor(){
        this.#ONE_MINUTE = 60  * this.#ONE_SECOND;
        this.#ONE_HOUR = 60  * this.#ONE_MINUTE;
        this.#ONE_DAY = 24  * this.#ONE_HOUR;
        // this.#ONE_WEEK = 8   * this.#ONE_DAY;
        // this.#ONE_MONTH = 30  * this.#ONE_DAY;
        // this.#ONE_YEAR = 360 * this.#ONE_DAY;

        // this.#MOON_CYCLE_PHASE = 12 * this.#VTIME_WEEK;
    }

    /******************************** MOON PHASES **********************************
 
     0% NM   7% WXC    40% FQM   57% WXG   90% FM  93% WNG  60% LQM  43% WNC  10% NM 
     2% NM  10% WXC    43% FQM   60% WXG   93% FM  90% WNG  57% LQM  40% WNC   7% NM
     5% NM  12% WXC    45% FQM   62% WXG   95% FM  88% WNG  55% LQM  38% WNC   5% NM
            14% WXC    48% FQM   64% WXG   98% FM  86% WNG  52% LQM  36% WNC   2% NM
            17% WXC    50% FQM   67% WXG  100% FM  83% WNG  50% LQM  33% WNC
            19% WXC    52% FQM   69% WXG   98% FM  81% WNG  48% LQM  31% WNC
            21% WXC    55% FQM   71% WXG   95% FM  79% WNG  45% LQM  29% WNC
            24% WXC              74% WXG           76% WNG           26% WNC
            26% WXC              76% WXG           74% WNG           24% WNC
            29% WXC              79% WXG           71% WNG           21% WNC
            31% WXC              81% WXG           69% WNG           19% WNC
            33% WXC              83% WXG           67% WNG           17% WNC
            36% WXC              86% WXG           64% WNG           14% WNC
            38% WXC              88% WXG           62% WNG           12% WNC
    ********************************************************************************/
    /**     
     * Private function - supports moon phase calculations 
     * @param vanatime (required) - Vanadiel time, in MILLISECONDS
     * @returns - integer - represents the day in the 84 day moon phase cycle
     */ 
    #moonDays(vanatime){ 
        if (vanatime === undefined || vanatime == null) vanatime = this.now();
        return  ((( vanatime /  this.#VTIME_DAY ) + 26) % this.#MOON_CYCLE_DAYS); 
    }  

    /**
     * Private function for doing arithmetic for moon phase percentage
     * @param vanatime (required) - Vanadiel time, in MILLISECONDS
     * @returns - integer representing moon phase percentage
     */ 
    #moonPercent(vanatime){ 
        if (vanatime === undefined || vanatime == null) vanatime = this.now();
        return Math.abs( -Math.round((42 - Math.floor(this.#moonDays(vanatime))) / 42 * 100) ); 
    }

    /**
     * Helper function for getting moon phase percentage
     * @param vanatime (required) - Vanadiel time, in MILLISECONDS
     * @returns - integer representing moon phase percentage
     */ 
    getMoonPercent(vanatime) { return this.#moonPercent(vanatime)};

    /**
     * @param vanatime - Vanadiel time, in MILLISECONDS; default value is now()
     * @returns - integer representing waxing/waning/neither
     */ 
    #moonDirection(vanatime){
        if (vanatime === undefined || vanatime == null) vanatime = this.now();
        var moondays = Math.floor(this.#moonDays(vanatime));
        //console.log(daysmod);
        if (moondays == 42 || moondays == 0) { return 0; }// neither waxing nor waning
        else if (moondays < 42){ return 1; } // waning
        else{ return 2; } // waxing
    }

    /**
     * @param vanatime - Vanadiel time, in MILLISECONDS; default value is now()
     * @returns - total milliseconds remaining until next conquest update
     */ 
    moonLatentPhase(vanatime){
        if (vanatime === undefined || vanatime == null) vanatime = this.now();
        var moonPhase = this.#moonPercent(vanatime);
        var moonDirection = this.#moonDirection(vanatime);

        //console.log("*mP", moonPhase);
        //console.log("*mD", moonDirection);
        
        if (moonPhase <= 5 || (moonPhase <= 10 && moonDirection == 1)) {return 0;} // New Moon - 10% waning -> 5% waxing
        else if (moonPhase >= 7 && moonPhase <= 38 && moonDirection == 2) {return 1;}  // Waxing Crescent - 7% -> 38% waxing
        else if (moonPhase >= 40 && moonPhase <= 55 && moonDirection == 2){return 2;}  // First Quarter - 40%% -> 55% waxing
        else if (moonPhase >= 57 && moonPhase <= 88 && moonDirection == 2){return 3;}  // Waxing Gibbous - 57% -> 88%
        else if (moonPhase >= 95 || (moonPhase >= 90 && moonDirection == 2)){return 4;}  // Full Moon - waxing 90% -> waning 95%
        else if (moonPhase >= 62 && moonPhase <= 93 && moonDirection == 1){return 5;}  // Waning Gibbous - 93% -> 62%
        else if (moonPhase >= 45 && moonPhase <= 60 && moonDirection == 1){return 6;}  // Last Quarter - 60% -> 45%
        else{return 7;}  // Waning Crescent - 43% -> 12%
    }

    /**
     * @returns - total milliseconds remaining until next conquest update
     */ 
    conquestRemainingTime(){ 
        var now =  new Date();
        //console.log(this.earthDate.getTime(), this.#VTIME_BIRTH);
        return (7 * (24 * 60 * 60 * 1000)) - ((now.getTime() - this.#VTIME_BIRTH) % (7 * (24 * 60 * 60 * 1000)));
    }

    /**
     * @returns - integer - total Vanadiel days remaining on current conquest
     */ 
    conquestRemainingVanaDays(){
        return Math.ceil(this.conquestRemainingTime() / (24 * 60 * 60 * 1000 / 25));
    }

    /**
     * @param time - Vanadiel time, in MILLISECONDS, needing to be converted 
     * @returns {Date} - value as Date() object
     */
    earthTime(time){
        if (time === undefined) time = this.now_inEarthMS();
        //else time = time * 60 * 1000;
        var earthTime = time / ( this.#VMULTIPLIER );
        return new Date(Math.floor(earthTime) - (this.#vanaBirthday - this.#VTIME_BIRTH));
    }


     /**
     * @param time - Vanadiel time, in minutes, needing to be converted
     * @returns - string, formatted 00:00
     */
     stringVanaTime(time){ 
        if (time === undefined || time === null) time = this.now();
        //console.log(time);
        var vYear = this.year(time);
        var vMon  = this.month(time)
        var vDate = this.date(time);
        var vHour = this.hour(time);
        var vMin  = this.mins(time);
        // var vSec  = time.getSeconds();
        // var vDay  = time.getDay();
        //seconds left our because we don't use them for any calcs

        if (vYear < 1000)  vYear = "0" + vYear; 
        if (vMon  < 10) vMon  = "0" + vMon;
        if (vDate  < 10)  vDate  = "0" + vDate;
        if (vHour < 10)  vHour = "0" + vHour; 
        if (vMin  < 10)  vMin  = "0" + vMin; 

        //return vYear + ":" + vMonth + ":" + vDate + " [" + vHour + ":" + vMin + "]" ; // for testing
        return vHour + ":" + vMin; 
    }
    
    /**
     * @param time - Vanadiel time, in milliseconds, needing to be converted
     * @returns - string, formatted 00:00:00
     */
    stringEarthTime(time) {
        var eTime = this.earthTime(time);  
        // const monthNames = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
        //Most of these arent needed, left in for future inclusion if needed/wanted
        // var eYear = eTime.getFullYear();
        // var eMon  = monthNames[eTime.getMonth()];
        var eDate = eTime.getDate();
        var eHour = eTime.getHours();
        var eMin  = eTime.getMinutes();
        var eSec  = eTime.getSeconds();
        var eDay  = eTime.getDay();
        
        // Assigns a leading zero if neccessary
        // if (eDate < 10)  eDate = "0" + eDate; 
        if (eHour < 10)  eHour = "0" + eHour; 
        if (eMin < 10)   eMin  = "0" + eMin;  
        if (eSec < 10)   eSec  = "0" + eSec;  

        // var str = eMon + " " + eDate + "," + eHour + ":" + eMin + ":" + eSec;
        var str = eHour + ":" + eMin + ":" + eSec;
        return str;
    }

    /**
     * @param  nextTime - Vana time, in MINUTES, needing to be converted
     * @returns - string of HOURS:MINS
     */
    timeUntil(nextTime){
        if (nextTime === undefined) { return "00:00:00"; }
        else if ( !(nextTime instanceof Date) ) {
            //console.log('!instance of Date');
            var e = this.earthTime(nextTime);
         } //earth time for next departure
        else e = nextTime;

        //console.log( e.getTime() );

        var now = new Date(); // earth time now
        
        //get difference between the next departure and current time... we want this to be in seconds, and Date()
        //and nextDeparture should be in milliseconds, so we /1000

        var days = (e.getTime() - now.getTime()) / (24 * 60 * 60 * 1000);
        var hours = (days - Math.floor(days)) * 24;
        var minutes = (hours - Math.floor(hours)) * 60;
        var seconds = Math.floor((minutes - Math.floor(minutes)) * 60);

        days = Math.floor(days);
        hours = Math.floor(hours);
        minutes = Math.floor(minutes);
        
        if (hours < 10)  hours = "0" + hours; 
        if (minutes < 10)   minutes  = "0" + minutes;  
        if (seconds < 10)   seconds  = "0" + seconds;  
        
        var str = minutes + ":" + seconds;

        if ( days > 0 ) { str = [days, hours, str].join(':'); }
        else if ( hours > 0 || typeof(hours) == 'string') { str = [hours, str].join(':'); }

        return str;
    }

    timer(time1, time2){
        var seconds = Math.floor((time1.getTime() - time2.getTime()) / 1000); 
                
        //basic math functions to get each element of time separated 
        var minutes = Math.floor(seconds / 60);
        var hours = Math.floor(minutes / 60);
        var days = Math.floor(hours / 24);

        hours = hours-(days * 24);
        minutes = minutes-(days * 24 * 60)-(hours * 60);
        seconds = seconds-(days * 24 * 60 * 60 )-(hours * 60 * 60)-(minutes * 60);

        seconds < 10 ? seconds = "0" + seconds : seconds;
        minutes < 10 ? minutes = "0" + minutes : minutes;
        hours < 10 ? hours = "0" + hours : hours;
        
        var str = hours + ":" + minutes + ":" + seconds;
        if ( days != 0) str = [days, str].join(':');
        return str; 
    }

}
const vanatime = new VanaTime();
let currentPopulation = 0;
////////////////////////////////////////////////////

function pageHasElement(element){
	
    function test(v){
        var i = document.getElementById(v);
        var c = document.getElementsByClassName(v);

        if ( i ) { return true; }
        if ( c[0] ) { return true; }
    }

    if (typeof(element) == 'string') return test(element);
    else { 
        for (const [k,v] of Object.entries(element) ){ 
            if (test(v) == true) return true;
        }
    }
    return false;
}

function updateSidebar() {
    if (!pageHasElement(timeElements.sidebar)) return;

    var hour = vanatime.hour();
    hour < 10 ? hour = ["0", hour].join('') : hour;
    var mins = vanatime.mins();
    mins < 10 ? mins = ["0", mins].join('') : mins;

    var sidebarTime = "<div><li><b>" + hour + ":" + mins + "</b>  ~  " + vanatime.year() + "-" + vanatime.month() + "-" + vanatime.date() + "</li>";
	sidebarTime +=	"<li><span style=\"font-weight: bold; font-size:14px; color:" + vanatime.dayColor() + "\" >"  + vanatime.dayLabel() + "</span><span style=\"font-size:14px;\">  ~ " + vanatime.moonPhaseIcon() + vanatime.getMoonPercent() + "%</span></li>"; 

    //sidebarTime +=	"<li>\u{1F7E2} Online ";
    if ( currentPopulation != 0 ) sidebarTime += "<li><span style=\"font-size:9px;\">\u{1F7E2}</span> Online <b>" + currentPopulation + "</b></li>";
    else sidebarTime += "<li><span style=\"font-size:9px;\">\u{1F534}</span> Online <i>~Standby~</i></li>";

    sidebarTime += "</div>";

	const sidebar_vanatime = document.getElementById(timeElements.sidebar);
    if (sidebar_vanatime) sidebar_vanatime.innerHTML = sidebarTime;
	//for (let i = 0; i < page_vanatime.length; i = i+1) { page_vanatime[i].innerHTML = sidebarTime; }
}

function updateBoatSchedule()  {

    if (!pageHasElement(timeElements.boats)) return;
    populateTransportSchedule(timeElements.boats.selbina, schedule.boats.selbina, 3);
    populateTransportSchedule(timeElements.boats.mhaura, schedule.boats.mhaura, 3);
    populateTransportSchedule(timeElements.boats.whitegate, schedule.boats.whitegate, 1);
    populateTransportSchedule(timeElements.boats.nashmau, schedule.boats.nashmau, 1);
    populateTransportSchedule(timeElements.boats.bibiki, schedule.boats.bibiki, 1);
    populateTransportSchedule(timeElements.boats.purgonorgoIsle, schedule.boats.purgonorgoIsle, 1);
    populateTransportSchedule(timeElements.boats.northLanding, schedule.boats.northLanding, 1);
    populateTransportSchedule(timeElements.boats.centralLanding, schedule.boats.centralLanding, 1);
    populateTransportSchedule(timeElements.boats.southLanding, schedule.boats.southLanding, 1);
}

function updateAirshipSchedule(){
    if (!pageHasElement(timeElements.airships)) return;

   populateTransportSchedule(timeElements.airships.jeuno, schedule.airships.jeuno, 1);
   populateTransportSchedule(timeElements.airships.bastok, schedule.airships.bastok, 1);
   populateTransportSchedule(timeElements.airships.sandy, schedule.airships.sandy, 1);
   populateTransportSchedule(timeElements.airships.windy, schedule.airships.windy, 1);
   populateTransportSchedule(timeElements.airships.kazham, schedule.airships.kazham, 1);
}
            


function _transportScheduleHeader(classname){

    var header = "<TABLE CLASS='" + classname + " vanatime-main-table" + "' CELLSPACING='0' CELLPADDING='0'>";
	header += "<TR><TH ALIGN='LEFT'>Destination</TH>";
	//header += "<TH ALIGN='LEFT'>Departure Day</TH>";
	header += "<TH ALIGN='LEFT'>VanaTime</TH>";
    header += "<TH ALIGN='LEFT'>Earth Time</TH>";
	header += "<TH ALIGN='LEFT'>Departs</TH></TR>";
	return header;
}

function _transportScheduleBody(schedule, entriesPerSchedule, numberOfEntries){
    function _helper(sched, n){
        var helperoffset = (sched[1] + sched[3] + sched[4]) * 60  * 1000 / 25;  // VANA MILLISECONDS
        //console.log("helperoffset: ", helperoffset, vanatime.now_inMS());
        while (helperoffset <  vanatime.now_inMS() ) {
            helperoffset += (sched[2] * n  * 60 * 1000 / 25);
        }
        return helperoffset; // VANA MILLISECONDS
    }

    var html = "";
    var offset = {};
    var rowhighlight = true;

    for( let n=1; n <= numberOfEntries; n++ ){ 
        html += '<TR class="vanatime-main-table-row-spacer"><TD></TD><TD></TD><TD></TD><TD></TD></TR>';
        for( let x=0; x < schedule.length; x++ ){ 
            const offsetVariable = x + (schedule.length * (n-1));

            for( let i=1 ; i <= entriesPerSchedule; i++ ){
                console.log(n,x,i, offsetVariable);

                if ( i > 1 ) offset[offsetVariable] +=  (schedule[x][2] * 60 * 1000 / 25);
                //else if ( lastOffset ) offset = lastOffset;
                else offset[offsetVariable] = _helper(schedule[x], n);

                // offset must be greater than the offset before it
                if ( offsetVariable >= schedule.length && offset[offsetVariable] <= offset[offsetVariable - schedule.length] )
                    {
                        console.log( "fired " + offsetVariable );
                        offset[offsetVariable] = offset[offsetVariable - schedule.length] + (schedule[x][2] * 60 * 1000 / 25);
                    }

                var earthdepTime = (vanatime.today_inMS() + (offset[offsetVariable] * 25));
                var vanadepTime = earthdepTime  / ( 60 * 1000);
                //var arrTime = depTime - ( sched[2][4] * 60 * 1000);  /// LEAVE THIS in the event someone wants to add ARRIVAL TIMES to future HTML tables

                //html += '<TR><TD>x= ' + x + '</TD><TD>' + vanatime.stringVanaTime(vanadepTime) + '</TD><TD>' + vanatime.stringEarthTime(earthdepTime) + '</TD><TD>' + vanatime.timeUntil(earthdepTime)  +'</TD></TR>';
                var rowclass = "";
                if ( rowhighlight ) rowclass = `class="vanatime-main-table-row-highlight"`;

                html += '<TR ' + rowclass + '><TD>' + schedule[x][0] + '</TD><TD>' + vanatime.stringVanaTime(vanadepTime) + '</TD><TD>' + vanatime.stringEarthTime(earthdepTime) + '</TD><TD>' + vanatime.timeUntil(earthdepTime)  +'</TD></TR>';
                rowhighlight = !rowhighlight;
            }
        }
    }
    return html;
}

function populateTransportSchedule(classname, schedule, entriesPerSchedule){
    const shipSched = document.getElementById(classname);
	if (shipSched) {
        var numberOfEntries = getSelectedNumberOfEntries(classname);
        if (numberOfEntries === 'undefined' || numberOfEntries === null || numberOfEntries == 0 ) numberOfEntries = 1;

        var _HTMLheader = _transportScheduleHeader(classname);
        var _HTMLbody = this._transportScheduleBody(schedule, entriesPerSchedule, numberOfEntries);
        var updatedSched =  _HTMLheader + _HTMLbody + "</TABLE>";

        const classname_details = classname + "-details";
        let t = shipSched.getElementsByClassName(classname_details)[0];
        var _HTMLdetails = '';
        if ( !t ){

            var temp = expandTableSelection(classname); 
            if ( !temp ) temp = "";
            const div = '<div class="vanatime-main-table">' + temp ;
            _HTMLdetails = `<span class="${classname_details}">${updatedSched}</span>`;
            shipSched.innerHTML =  div + _HTMLdetails + '</div>';

        }
        else {
            t.innerHTML = updatedSched;
        }
        
    }
}

function updateGuilds(){
    if (!pageHasElement(timeElements.guilds)) return;

    /* ALCHEMY */
	const page_alchemy_hours = document.getElementById(timeElements.guilds.alchemy);
    if ( page_alchemy_hours ) { page_alchemy_hours.innerHTML = _guildSchedule(schedule.guilds.alchemy);  }

    const page_alchemy_status = document.getElementById(timeElements.guilds.alchemy_status);
    if ( page_alchemy_status ) { page_alchemy_status.innerHTML = _guildSchedule(schedule.guilds.alchemy, timeElements.guilds.alchemy_status);  }

    const page_alchemy_holiday = document.getElementById(timeElements.guilds.alchemy_holiday);
    if ( page_alchemy_holiday ) { page_alchemy_holiday.innerHTML = _guildSchedule(schedule.guilds.alchemy, timeElements.guilds.alchemy_holiday);  }

    const page_alchemy_timer = document.getElementById(timeElements.guilds.alchemy_timer);
    if ( page_alchemy_timer ) { page_alchemy_timer.innerHTML = _guildSchedule(schedule.guilds.alchemy, timeElements.guilds.alchemy_timer);  }


    /* BONECRAFT */
	const page_bonecraft_hours = document.getElementById(timeElements.guilds.bonecrafting);
	if ( page_bonecraft_hours ) { page_bonecraft_hours.innerHTML = _guildSchedule(schedule.guilds.bonecrafting);  }

    const page_bonecraft_status = document.getElementById(timeElements.guilds.bonecraft_status);
    if ( page_bonecraft_status ) { page_bonecraft_status.innerHTML = _guildSchedule(schedule.guilds.bonecrafting, timeElements.guilds.bonecraft_status);  }

    const page_bonecraft_holiday = document.getElementById(timeElements.guilds.bonecraft_holiday);
    if ( page_bonecraft_holiday ) { page_bonecraft_holiday.innerHTML = _guildSchedule(schedule.guilds.bonecrafting, timeElements.guilds.bonecraft_holiday);  }

    const page_bonecraft_timer = document.getElementById(timeElements.guilds.bonecraft_timer);
    if ( page_bonecraft_timer ) { page_bonecraft_timer.innerHTML = _guildSchedule(schedule.guilds.bonecrafting, timeElements.guilds.bonecraft_timer);  }


    /* CLOTHCRAFT */
	const page_clothcraft_hours = document.getElementById(timeElements.guilds.clothcrafting);
	if ( page_clothcraft_hours ) { page_clothcraft_hours.innerHTML = _guildSchedule(schedule.guilds.clothcrafting);  }

    const page_clothcraft_status = document.getElementById(timeElements.guilds.clothcraft_status);
    if ( page_clothcraft_status ) { page_clothcraft_status.innerHTML = _guildSchedule(schedule.guilds.clothcrafting, timeElements.guilds.clothcraft_status);  }

    const page_clothcraft_holiday = document.getElementById(timeElements.guilds.clothcraft_holiday);
    if ( page_clothcraft_holiday ) { page_clothcraft_holiday.innerHTML = _guildSchedule(schedule.guilds.clothcrafting, timeElements.guilds.clothcraft_holiday);  }

    const page_clothcraft_timer = document.getElementById(timeElements.guilds.clothcraft_timer);
    if ( page_clothcraft_timer ) { page_clothcraft_timer.innerHTML = _guildSchedule(schedule.guilds.clothcrafting, timeElements.guilds.clothcraft_timer);  }


    /* COOKING */
	const page_cooking_hours = document.getElementById(timeElements.guilds.cooking);
	if ( page_cooking_hours ) { page_cooking_hours.innerHTML = _guildSchedule(schedule.guilds.cooking);  }

    const page_cooking_status = document.getElementById(timeElements.guilds.cooking_status);
    if ( page_cooking_status ) { page_cooking_status.innerHTML = _guildSchedule(schedule.guilds.cooking, timeElements.guilds.cooking_status);  }

    const page_cooking_holiday = document.getElementById(timeElements.guilds.cooking_holiday);
    if ( page_cooking_holiday ) { page_cooking_holiday.innerHTML = _guildSchedule(schedule.guilds.cooking, timeElements.guilds.cooking_holiday);  }

    const page_cooking_timer = document.getElementById(timeElements.guilds.cooking_timer);
    if ( page_cooking_timer ) { page_cooking_timer.innerHTML = _guildSchedule(schedule.guilds.cooking, timeElements.guilds.cooking_timer);  }
	

    /* FISHING */
	const page_fishing_hours = document.getElementById(timeElements.guilds.fishing);
	if ( page_fishing_hours ) { page_fishing_hours.innerHTML = _guildSchedule(schedule.guilds.fishing);  }

    const page_fishing_status = document.getElementById(timeElements.guilds.fishing_status);
    if ( page_fishing_status ) { page_fishing_status.innerHTML = _guildSchedule(schedule.guilds.fishing, timeElements.guilds.fishing_status);  }

    const page_fishing_holiday = document.getElementById(timeElements.guilds.fishing_holiday);
    if ( page_fishing_holiday ) { page_fishing_holiday.innerHTML = _guildSchedule(schedule.guilds.fishing, timeElements.guilds.fishing_holiday);  }

    const page_fishing_timer = document.getElementById(timeElements.guilds.fishing_timer);
    if ( page_fishing_timer ) { page_fishing_timer.innerHTML = _guildSchedule(schedule.guilds.fishing, timeElements.guilds.fishing_timer);  }
	

    /* GOLDSMITHING */
	const page_goldsmithing_hours = document.getElementById(timeElements.guilds.goldsmithing);
	if ( page_goldsmithing_hours ) {  page_goldsmithing_hours.innerHTML = _guildSchedule(schedule.guilds.goldsmithing);  }

    const page_goldsmithing_status = document.getElementById(timeElements.guilds.goldsmithing_status);
    if ( page_goldsmithing_status ) { page_goldsmithing_status.innerHTML = _guildSchedule(schedule.guilds.goldsmithing, timeElements.guilds.goldsmithing_status);  }

    const page_goldsmithing_holiday = document.getElementById(timeElements.guilds.goldsmithing_holiday);
    if ( page_goldsmithing_holiday ) { page_goldsmithing_holiday.innerHTML = _guildSchedule(schedule.guilds.goldsmithing, timeElements.guilds.goldsmithing_holiday);  }

    const page_goldsmithing_timer = document.getElementById(timeElements.guilds.goldsmithing_timer);
    if ( page_goldsmithing_timer ) { page_goldsmithing_timer.innerHTML = _guildSchedule(schedule.guilds.goldsmithing, timeElements.guilds.goldsmithing_timer);  }
	

    /* LEATHERCRAFT */
	const page_leathercraft_hours = document.getElementById(timeElements.guilds.leathercrafting);
	if ( page_leathercraft_hours ) { page_leathercraft_hours.innerHTML = _guildSchedule(schedule.guilds.leathercrafting);  }

    const page_leathercraft_status = document.getElementById(timeElements.guilds.leathercraft_status);
    if ( page_leathercraft_status ) { page_leathercraft_status.innerHTML = _guildSchedule(schedule.guilds.leathercrafting, timeElements.guilds.leathercraft_status);  }

    const page_leathercraft_holiday = document.getElementById(timeElements.guilds.leathercraft_holiday);
    if ( page_leathercraft_holiday ) { page_leathercraft_holiday.innerHTML = _guildSchedule(schedule.guilds.leathercrafting, timeElements.guilds.leathercraft_holiday);  }

    const page_leathercraft_timer = document.getElementById(timeElements.guilds.leathercraft_timer);
    if ( page_leathercraft_timer ) { page_leathercraft_timer.innerHTML = _guildSchedule(schedule.guilds.leathercrafting, timeElements.guilds.leathercraft_timer);  }


    /* SMITHING */
	const page_smithing_hours = document.getElementById(timeElements.guilds.smithing);
	if ( page_smithing_hours ) { page_smithing_hours.innerHTML = _guildSchedule(schedule.guilds.smithing); } 

    const page_smithing_status = document.getElementById(timeElements.guilds.smithing_status);
    if ( page_smithing_status ) { page_smithing_status.innerHTML = _guildSchedule(schedule.guilds.smithing, timeElements.guilds.smithing_status);  }

    const page_smithing_holiday = document.getElementById(timeElements.guilds.smithing_holiday);
    if ( page_smithing_holiday ) { page_smithing_holiday.innerHTML = _guildSchedule(schedule.guilds.smithing, timeElements.guilds.smithing_holiday);  }

    const page_smithing_timer = document.getElementById(timeElements.guilds.smithing_timer);
    if ( page_smithing_timer ) { page_smithing_timer.innerHTML = _guildSchedule(schedule.guilds.smithing, timeElements.guilds.smithing_timer);  }
	

    /* WOODWORKING */
	const page_woodworking_hours = document.getElementById(timeElements.guilds.woodworking);
	if ( page_woodworking_hours ) { page_woodworking_hours.innerHTML = _guildSchedule(schedule.guilds.woodworking); } 

    const page_woodworking_status = document.getElementById(timeElements.guilds.woodworking_status);
    if ( page_woodworking_status ) { page_woodworking_status.innerHTML = _guildSchedule(schedule.guilds.woodworking, timeElements.guilds.woodworking_status);  }

    const page_woodworking_holiday = document.getElementById(timeElements.guilds.woodworking_holiday);
    if ( page_woodworking_holiday ) { page_woodworking_holiday.innerHTML = _guildSchedule(schedule.guilds.woodworking, timeElements.guilds.woodworking_holiday);  }

    const page_woodworking_timer = document.getElementById(timeElements.guilds.woodworking_timer);
    if ( page_woodworking_timer ) { page_woodworking_timer.innerHTML = _guildSchedule(schedule.guilds.woodworking, timeElements.guilds.woodworking_timer);  }

    const page_allguilds_hours = document.getElementById(timeElements.guilds.all);
	if ( page_allguilds_hours ) {  
        
        var guildOut = "<TABLE CLASS='"+ timeElements.guilds.all + " vanatime-main-table" + "' CELLSPACING='0' CELLPADDING='0' border='1px solid black'>";
        guildOut = guildOut + "<TR><TH ALIGN='center' WIDTH=100 >Guild</TH>";
        guildOut = guildOut + "<TH ALIGN='center' WIDTH=175 >Status</TH></TR>";
        guildOut = guildOut + "<TR><TH> Alchemy</TH><td>" +         _guildSchedule(schedule.guilds.alchemy)         + "</td></TR>";
        guildOut = guildOut + "<TR><TH> Blacksmithing</TH><td>" +   _guildSchedule(schedule.guilds.smithing) 	    + "</td></TR>";
        guildOut = guildOut + "<TR><TH> bonecrafting</TH><td>" +     _guildSchedule(schedule.guilds.bonecrafting) 	+ "</td></TR>";
        guildOut = guildOut + "<TR><TH> Goldsmithing</TH><td>" +    _guildSchedule(schedule.guilds.goldsmithing) 	+ "</td></TR>";
        guildOut = guildOut + "<TR><TH> Clothcrafting</TH><td>" +   _guildSchedule(schedule.guilds.clothcrafting) 	+ "</td></TR>";
        guildOut = guildOut + "<TR><TH> Woodworking</TH><td>" +     _guildSchedule(schedule.guilds.woodworking) 	+ "</td></TR>";
        guildOut = guildOut + "<TR><TH> Leatherworking </TH><td>" + _guildSchedule(schedule.guilds.leathercrafting) + "</td></TR>";
        guildOut = guildOut + "<TR><TH> Fishing</TH><td>" +         _guildSchedule(schedule.guilds.fishing) 	    + "</td></TR>";
        guildOut = guildOut + "<TR><TH> Cooking</TH><td>" +         _guildSchedule(schedule.guilds.cooking) 	    + "</td></TR>";
        guildOut = guildOut + "</TABLE>";
    
        page_allguilds_hours.innerHTML = guildOut; 
    } 
}

function _guildStatusCheck(guild, statusID) {

    return outputText = [outputTxt1, outputTxt2];
}

function _guildSchedule(guild, statusID) {
    
    if (guild === 'undefined') return "_guildSchedule: error";
    
    var now = vanatime.now_inMS();
    var guildOpens = guild[0] * 60 * 1000 / 25;
    var guildCloses = guild[1] * 60 * 1000 / 25;

    // Guild open/close check
    var nextOpenTime = 0;
    var outputTxt1 = "", outputTxt2 = "";
    if (guildOpens >= now) {
        nextOpenTime = (((guildOpens - now) * 25) + vanatime.now_inEarthMS());
        outputTxt1 = "Opens in: ";
        outputTxt2 = "Currently Closed. Open tomorrow.";
    } else if ((guildOpens < now) && (guildCloses > now)) {
        nextOpenTime = (((guildCloses - now) * 25) + vanatime.now_inEarthMS());
        outputTxt1 = "Closes in: ";
        outputTxt2 = "Currently Open for business.";
    } else if (guildCloses <= now)  {
        nextOpenTime = ((24 * 60 * 60 * 1000 / 25) - now  + guildOpens) * 25 + vanatime.now_inEarthMS();
        outputTxt1 = "Opens in: "; 
        outputTxt2 = "Currently Closed. Open tomorrow.";
    }

    if(typeof(statusID) == 'string' && statusID.includes('status')) {
        // outputTxt2 = [`<span id=\'${statusID}\'>`, outputTxt2].join(''); 
        // outputTxt2 = [outputTxt2, '</span'].join('');
        return outputTxt2;
    }
    

    // Holiday check 
    if ((guild[2] == vanatime.weekDay()) && (guildCloses > now)) {
        nextOpenTime = ((24 * 60 * 60 * 1000 / 25)  - now + guildOpens) * 25 + vanatime.now_inEarthMS();
        outputTxt2 = "Currently Closed for Guild Holiday.";
        outputTxt1 = "Opens in: ";
        if(typeof(statusID) == 'string' &&statusID.includes('holiday')) {
            // outputTxt2 = [`<span id=\'${statusID}\'>`, outputTxt2].join(''); 
            // outputTxt2 = [outputTxt2, '</span'].join('');
            return outputTxt2;
        }
    } else if (((vanatime.weekDay() + 1) == guild[2]) && (guildCloses <= now))  {
        nextOpenTime = ((24 * 60 * 60 * 1000 / 25)  - now + guildOpens) * 25 + (24 * 60 * 60 * 1000 / 25) + vanatime.now_inEarthMS();
        outputTxt2 = "Currently Closed. Guild Holiday tomorrow.";
        outputTxt1 = "Opens in: ";
        if(typeof(statusID) == 'string' && statusID.includes('holiday')) {
            // outputTxt2 = [`<span id=\'${statusID}\'>`, outputTxt2].join(''); 
            // outputTxt2 = [outputTxt2, '</span'].join('');
            return outputTxt2;
        }
    }

    if(typeof(statusID) == 'string' &&statusID.includes('holiday')) {
        // outputTxt2 = [`<span id=\'${statusID}\'>`, outputTxt2].join(''); 
        // outputTxt2 = [outputTxt2, '</span'].join('');
        return `Guild is not on holiday until ${vanatime.dayLabel(guild[2])}.`;
    }

    /****DEBUGGING*****/
    // if (guild == schedule.guilds.fishing) {
    //     console.log(guild[2], nextOpenTime, now, vanatime.timeUntil(nextOpenTime) );
    // }
    /****DEBUGGING*****/

    if(typeof(statusID) == 'string' && statusID.includes('timer')) {
        return outputTxt1 + vanatime.timeUntil(nextOpenTime);
    }
    return outputTxt1 + vanatime.timeUntil(nextOpenTime) + ". " + outputTxt2;

}

function updateConquest()  {
    if (!pageHasElement(timeElements.conquest)) return;
    
    function stringNextConquest(remaining){
        //if (nextConquest === undefined) { return "stringConquestTimer: timer undefined"; }
        const now = new Date();
        remaining = now.getTime() + remaining;

        var nextConquest = new Date(remaining);
        var tempHour = nextConquest.getHours();
        var tempMin  = nextConquest.getMinutes();
        var tempSec  = nextConquest.getSeconds();

        if (tempHour < 10)  tempHour = "0" + tempHour; 
        if (tempMin < 10)   tempMin  = "0" + tempMin;  
        if (tempSec < 10)   tempSec  = "0" + tempSec;  

        var strNextConquest = nextConquest.toDateString() + " " + tempHour + ":" + tempMin + ":" + tempSec;

            // var remaining = (temp.getTime() - now.getTime()) / (24 * 60 * 60 * 1000);
            // var hours = (remaining - Math.floor(remaining)) * 24;
            // var mins = (hours - Math.floor(hours)) * 60;
            // var secs = Math.floor((mins - Math.floor(mins)) * 60);

            // remaining = Math.floor(remaining);
            // hours = Math.floor(hours);
            // mins = Math.floor(mins);

            // if (hours < 10)  hours = "0" + hours; 
            // if (mins < 10)   mins  = "0" + mins;  
            // if (secs < 10)   secs  = "0" + secs;  

            // // tempDays < 10 ? ["0", tempDays].join(':') : tempDays;
            // // tempDays > 0 ? [tempDays, strTimer].join(':') : null;

            // strDiff = hours + ":" + mins + ":" + secs;

        //return strNextConquest + "(" + strDiff + ")";
        return strNextConquest + "(" + vanatime.timeUntil(nextConquest) + ")";
    }
    
    const now = new Date();
    var remaining = vanatime.conquestRemainingTime();
    remainingVanaDaysOnConquest = vanatime.conquestRemainingVanaDays();
    
    conq = remainingVanaDaysOnConquest + ' Vana´diel Days <BR>';
    conq += stringNextConquest(remaining); //+ ' (' + timer.getHours() + ":" + timer.getMinutes() + ":" + timer.getSeconds() + ')';

    const conquest_time = document.getElementById(timeElements.conquest);
    if (conquest_time) conquest_time.innerHTML = conq; 
}


function updateMoonPhaseSchedule(){
    if (!pageHasElement(timeElements.moonSchedule)) return;

    populateMoonPhaseSchedule(timeElements.moonSchedule, 7);
}

function _moonPhaseScheduleHeader(classname){
    var header = "<TABLE CLASS='" + classname + " vanatime-main-table" + "' WIDTH='500' CELLSPACING='0' CELLPADDING='0'>";
	header += "<TR ><TH ALIGN='LEFT'>Moon Phase</TH>";
	header += "<TH ALIGN='LEFT'>Start Time</TH>";
	header += "<TH ALIGN='LEFT'>End Time</TH>";
    //header += "<TH ALIGN='LEFT'>Phase Ends in...</TH>";
    header += "</TR>";
	return header;
}

function _moonPhaseScheduleBody(numberOfEntries){
    var html = "";
    
    if ( numberOfEntries === undefined || numberOfEntries === null ) numberOfEntries = 7;

    // DEBUGGING
    numberOfEntries = 7;

        var vTempTime = vanatime.today_inMS() / (60 * 1000); // VANA TIME IN TOTAL MINUTES
        var thisMoonPhase = vanatime.moonLatentPhase(vTempTime); 

        var lunarOffset = 0, 
            _time;
        for(var x = 0 ; x < numberOfEntries; x++ ){

            if ( x > 0 ){
                lunarOffset += 7;
                if ( thisMoonPhase == 1 || thisMoonPhase == 3 || thisMoonPhase == 5 || thisMoonPhase == 7) {
                    _time = vTempTime + (60 * 24 * lunarOffset);
                    if ( vanatime.moonLatentPhase(_time) == thisMoonPhase) lunarOffset += 7;
                }
                //console.log(thisMoonPhase);

                thisMoonPhase++;
                if (thisMoonPhase > 7) thisMoonPhase = 0;
                //console.log(thisMoonPhase);
            }

            var phaseStartTime;
            for( var i = 1 ; i >= -13 ; i-- ) {
                _time = vTempTime + (60 * 24 * lunarOffset) + (60 * 24 * (i - 1)); // VANA MINUTES
            //console.log("s", thisMoonPhase, lunarOffset, vanatime.moonLatentPhase(_time));
                
                if ( vanatime.moonLatentPhase(_time) != thisMoonPhase ) {
                    //console.log( "start", i, vanatime.moonLatentPhase(_time), thisMoonPhase );
                    break;
                }
                else phaseStartTime = _time * 60; // VANA SECONDS
            }
            

            var phaseEndTime; 
            for( var i = -1 ; i <= 14 ; i++ ) {
                _time = vTempTime + (60 * 24 * lunarOffset) + (60 * 24 * (i + 1));// VANA MINUTES
            //console.log("e", thisMoonPhase,lunarOffset,vanatime.moonLatentPhase(_time));
            
                if ( vanatime.moonLatentPhase(_time) != thisMoonPhase ) {
                    //console.log( "end", i, vanatime.moonLatentPhase(_time), thisMoonPhase );
                    break;
                }
                else phaseEndTime = (_time + (60 * 24)) * 60; // VANA SECONDS
                // for loop breaks when _time is at 00:00 for the day the moon phase changes... 
                // we add another day to this (60 + 24) to get the start of the next day for the table
            } 
            

            const startDate = new Date(Math.floor(phaseStartTime / (25 / 1000)) - vanatime.getDifference() );
            const endDate = new Date(Math.floor(phaseEndTime / (25 / 1000)) - vanatime.getDifference() );

            var strDetails = vanatime.moonPhaseIcon(thisMoonPhase) + " " + vanatime.moonPhaseName(thisMoonPhase) + " "  + vanatime.moonPhasePercentages(thisMoonPhase);            

            function dateString(date){
                var sec = date.getSeconds(), hrs = date.getHours(), mins = date.getMinutes(); 
                sec < 10 ? sec = "0" + sec : sec;
                hrs < 10 ? hrs = "0" + hrs : hrs;
                mins < 10 ? mins = "0" + mins : mins;
                return vanatime.weekdayLabel(date.getDay()) + ", " + date.getDate() + " " + vanatime.monthLabel(date.getMonth()) + " " + hrs + ":" + mins + ":" + sec;
            }

            html += '<TR ><TD>' + strDetails + '</TD><TD>' + dateString(startDate) + '</TD><TD>' + dateString(endDate)  + '</TD>';

            // const now = new Date();
            // var strTimer = vanatime.timer(endDate, now);
            // html += '<TD>' + strTimer + '</TD>';

            html += '</TR>';
        }
    return html;
}

function populateMoonPhaseSchedule(classname, numberOfEntries){
    
    
    const moonSchedule = document.getElementById(classname);
    if (moonSchedule) {
        numberOfEntries = Number(moonSchedule.getAttribute('data-entries'));
        if (numberOfEntries === undefined || numberOfEntries === null || numberOfEntries == 0 ) numberOfEntries = 7;
        else numberOfEntries = numberOfEntries * 7;
        var _HTMLheader = this._moonPhaseScheduleHeader(classname);
        var _HTMLbody = this._moonPhaseScheduleBody(numberOfEntries);
        moonSchedule.innerHTML = _HTMLheader + _HTMLbody + "</TABLE>";
    }
}


function updateRSE()  {
    const rseclass = timeElements.rseSchedule;
    const rseSched = document.getElementById(rseclass);
	if (!rseSched) return;
    
        function formatDate(varTime, showDay) {

            var varDate = new Date(varTime);
            var yyyy = varDate.getYear();
        
            var mm = varDate.getMonth() + 1;
            if (mm < 10) { mm = "0" + mm; }
        
            var dd = varDate.getDate();
            if (dd < 10) { dd = "0" + dd; }
        
            var day = varDate.getDay();
        
            var hh = varDate.getHours();
            
            if (hh < 10) { hh = "0" + hh; }
        
            var min = varDate.getMinutes();
            if (min < 10) { min = "0" + min; }
        
            var ss = varDate.getSeconds();
            if (ss < 10) { ss = "0" + ss; }
            if (showDay == 1)  {
            dateString = vanatime.weekdayLabel(day) + ", " + vanatime.monthLabel(mm-1) + ' ' + dd + ', ' + yyyy + " " + hh + ":" + min + ":" + ss;
            } else if (showDay == 2)  {
            dateString = vanatime.monthLabel(mm-1) + " " + dd + ",  " + hh + ":" + min + ":" + ss;
            }
            return dateString;
        }
     
    var timenow = new Date();
    var localtime = timenow.getTime();

    // ???
    var repeatCal = 8;
    
    var rowclass;
    var rowhighlight = true;

    RSECal = "<TABLE CLASS=\'" + rseclass + " vanatime-main-table" + "\' WIDTH='500' CELLSPACING='0' CELLPADDING='0'><TR><TH ALIGN='LEFT'>Date & Time</TH><TH ALIGN='LEFT'>Race</TH><TH ALIGN='LEFT'>Location</TH></TR>"
    
    var numberOfEntries = getSelectedNumberOfEntries(rseclass);
    if (numberOfEntries === 'undefined' || numberOfEntries === null || numberOfEntries == 0 ) numberOfEntries = 1;

    numberOfEntries = 1;

    for( let n=1; n <= numberOfEntries; n++ ){ 
        
       for ( i = 0; i < repeatCal; i++) {
          elapsedWeeks = Math.floor( (localtime - vanatime.rseDate) / (8 * (24 * 60 * 60 * 1000 / 25)) ) + i;
          RSEstart = vanatime.rseDate + (elapsedWeeks * 8 * (24 * 60 * 60 * 1000 / 25));
          
          if ( rowhighlight ) rowclass = `class="vanatime-main-table-row-highlight"`;
          else rowclass = "";

          RSECal = RSECal + "<TR " + rowclass + "><TD>" + formatDate(RSEstart,2) + '</TD><TD>' + vanatime.rseRace(elapsedWeeks % 8) + '</TD><TD>';
          //RSECal = RSECal + "<A HREF=#  onmousedown='javascript:getRSEDetails(" + (elapsedWeeks % 3) + ")'>";
          RSECal = RSECal + vanatime.rseLocation(elapsedWeeks % 3);
          RSECal = RSECal + '</A></TD></TR>';
          rowhighlight = !rowhighlight;
       }
    //   if (repeatCal < 1) { RSECal = ""; } 
    //   else { RSECal = RSECal + '</TABLE>'; }
    RSECal += '<TR class="vanatime-main-table-row-spacer"><TD></TD><TD></TD><TD></TD></TR>';

    }
    RSECal = RSECal + '</TABLE>';
    // else {
    //    RSECal = "<TABLE CLASS='blank' CELLPADDING='0' CELLSPACING='0'><TR><TH WIDTH='120' ALIGN='LEFT'>Start</TH><TH WIDTH='120' ALIGN='LEFT'>End</TH><TH WIDTH='60' ALIGN='LEFT'>Location</TH></TR>"
    //    offsetTime = race * 8 * msGameDay;
 
    //    for ( i = 0; i < repeatCal; i++) {
    //       elapsedWeeks = Math.floor( (localtime - vanatime.rseDate) / (64 * msGameDay) ) + i;
          
    //       elapsedLocationWeeks = Math.floor( (localtime - vanatime.rseDate) / (8 * msGameDay) ) + (8 * i);
    //       raceOffset = race - (elapsedLocationWeeks % 8);
    //       elapsedLocationWeeks = elapsedLocationWeeks + raceOffset;
 
    //       RSEstart = vanatime.rseDate + (elapsedWeeks * 64 * msGameDay) + offsetTime ;
    //       RSEend = RSEstart + (8 * msGameDay);
    //       RSECal = RSECal + "<TR><TD>" + formatDate(RSEstart,2) + "</TD><TD>" + formatDate(RSEend,2) + "</TD><TD>";
    //       //RSECal = RSECal + "<A HREF=#  onmousedown='javascript:getRSEDetails(" + (elapsedLocationWeeks % 3) + ")'>";
    //       RSECal = RSECal + RSEloc[(elapsedLocationWeeks) % 3] 
    //       RSECal = RSECal + "</A></TD></TR>";
    //    }
    //   if (repeatCal < 1) { RSECal = ""; } else { RSECal = RSECal + '</TABLE>'; }
 
    // }

    const classname_details = rseclass + "-details";
    let t = document.getElementsByClassName(classname_details)[0];
    var _HTMLdetails = '';
    //console.log(t);
    if ( t === 'undefined' || t === null || !t ){
        
        var temp = expandTableSelection(rseclass); 
        if ( !temp ) temp = "";
        const div = '<div class="vanatime-main-table">' + temp ;
        _HTMLdetails = `<span class="${classname_details}">${RSECal}</span>`;
        rseSched.innerHTML =  div + _HTMLdetails + '</div>';

    }
    else {
        console.log(t);
        t.innerHTML = RSECal;
    }
    
    //document.getElementById(rseclass).innerHTML = RSECal;
 }

function expandTableSelection(classname){

    const classnameheader = classname + "-header";
    const classnameshowSelect = classname + "-showSelect";

    if ( classname == timeElements.rseSchedule ) {
        return `
        <div class="${classnameheader}" style="background:#FFFFFF00; width: max-content; float: right;">Shown:
        <select id="${classnameshowSelect}" style="background:#DFDFDF50; ">
            <option value="0">All </option>
            <option value="1">M. Hume</option>
            <option value="2">F. Hume</option>
            <option value="3">M. Elvaan</option>
            <option value="4">F. Elvaan</option>
            <option value="5">M. TaruTaru</option>
            <option value="6">F. TaruTaru</option>
            <option value="7">Mithra</option>
            <option value="8">Galka</option>

        </select>
        </div>
    `;
    }

    return `
        <div class="${classnameheader}" style="background:#FFFFFF00; width: max-content; float: right;">Shown:
        <select id="${classnameshowSelect}" style="background:#DFDFDF50; ">
            <option value="0">1 </option>
            <option value="1">3</option>
            <option value="2">5</option>
            <option value="3">10</option>
        </select>
        </div>
    `;
}

function expandRaceTable(classname){
    const classnameheader = classname + "-header";
    const classnameshowSelect = classname + "-showSelect";

    const temp = `
        <div class="${classnameheader}" style="background:#FFFFFF00; width: max-content; float: right;">Shown:
        <select id="${classnameshowSelect}" style="background:#DFDFDF50; ">
            <option value="0">1 </option>
            <option value="1">3</option>
            <option value="2">5</option>
            <option value="3">10</option>
        </select>
        </div>
    `;
    
    // return popupButton;
    return temp;
}

function getSelectedNumberOfEntries(classname){
    // Number of entries comes from selection dropdown
    var e = document.getElementById(classname + "-showSelect");
    if ( !e ) return 1;
    var entries;
    switch(e.value) {
        case "1":
            entries = 3;
          break;
        case "2":
            entries = 5;
          break;
        case "3":
            entries = 10;
          break;
        default:
            entries = 1;
      } 
    //console.log(classname + ":" + entries);
    return entries;
}

function gametick(){
    
    updateSidebar();
    updateAirshipSchedule();
    updateBoatSchedule();
    updateGuilds();
	updateConquest();
    updateMoonPhaseSchedule();
    updateRSE();

	setTimeout("gametick()", 1000);
    //console.log('gametick');
}

gametick();

async function fetchWithTimeout(resource, options = {}) {
    const { timeout = 10000 } = options;

    const controller = new AbortController();
    const id = setTimeout(() => controller.abort(), timeout);

    const response = await fetch(resource, {
      ...options,
      signal: controller.signal
    });
    clearTimeout(id);

    return response;
  }

async function populationFetch(){

    try {
        const response = await fetchWithTimeout('https://api.horizonxi.com/api/v1/misc/exp-sync-status', {});
        const fetchedPopulation = await response.json();
        //if ( typeof(fetchedPopulation) == 'number' ) currentPopulation = fetchedPopulation;
        //console.log(fetchedPopulation);
        //return games;
        //console.log('populationFetchTick', fetchedPopulation);
        //populationFetch();
        currentPopulation = fetchedPopulation;
        setTimeout("startPopulationFetch()", 300000);
      } catch (error) {
        //fetched = 0;
        console.log(error.name === 'AbortError');
        startPopulationFetch();
      }
      //console.log('currentPopulation = ', currentPopulation);

      //return fetched;
}

function startPopulationFetch(){
    //console.log('startPopulationFetch');
    populationFetch();
    // if ( currentPopulation == 0) startPopulationFetch();
    // else setTimeout("startPopulationFetch()", 7000);
}
startPopulationFetch();