<?php
date_default_timezone_set
('America/Denver');
$errors "";
$successes "";
$minFileDate "";

function 
D($data)
{
    
header('Content-type: text/plain');
    echo 
print_r($data1);
    die();
}

function 
captcha()
{
    
$permitted_chars 'ABCDEFGHJKMNPQRSTUVWXYZ';

    function 
secure_generate_string($input$strength 5$secure true) {
        
$input_length strlen($input);
        
$random_string '';
        for(
$i 0$i $strength$i++) {
            if(
$secure) {
                
$random_character $input[random_int(0$input_length 1)];
            } else {
                
$random_character $input[mt_rand(0$input_length 1)];
            }
            
$random_string .= $random_character;
        }
        return 
$random_string;
    }

    
$image imagecreatetruecolor(20050);

    
imageantialias($imagetrue);

    
$colors = [];

    
$red rand(125175);
    
$green rand(125175);
    
$blue rand(125175);

    for(
$i 0$i 5$i++) {
        
$colors[] = imagecolorallocate($image$red 20*$i$green 20*$i$blue 20*$i);
    }

    
imagefill($image00$colors[0]);

    for(
$i 0$i 10$i++) {
        
imagesetthickness($imagerand(210));
        
$line_color $colors[rand(14)];
        
imagerectangle($imagerand(-10190), rand(-1010), rand(-10190), rand(4060), $line_color);
    }

    
$black imagecolorallocate($image000);
    
$white imagecolorallocate($image255255255);
    
$textcolors = [$black$white];

    
$fonts = [dirname(__FILE__).'/fonts/Acme.ttf'dirname(__FILE__).'/fonts/Ubuntu.ttf'dirname(__FILE__).'/fonts/Merriweather.ttf'dirname(__FILE__).'/fonts/PlayfairDisplay.ttf'];

    
$string_length 6;
    
$captcha_string secure_generate_string($permitted_chars$string_length);

    
$_SESSION['captcha_text'] = $captcha_string;
    for(
$i 0$i $string_length$i++) {
        
$letter_space 170/$string_length;
        
$initial 15;
        
imagettftext($image24rand(-1515), $initial $i*$letter_spacerand(2545), $textcolors[rand(01)], $fonts[array_rand($fonts)], $captcha_string[$i]);
    }

    
header('Content-type: image/png');
    
imagepng($image);
    
imagedestroy($image);
}

function 
copyrightYears()
{
    
$copyYear 2008
    
$curYear date('Y'); 
    return 
$copyYear . (($copyYear != $curYear) ? '-' $curYear '');
}

function 
counter()
{
    global 
$errors;
    try
    {
        if (!
class_exists('Database'))
        {
            require 
__DIR__.'/counterdb.php';
        }
        
$db_connection = new Database();
        
$conn $db_connection->dbConnection();
        
$user_ip $_SERVER['REMOTE_ADDR'];
        
$actual_link strtok($_SERVER['REQUEST_URI'], '?');
        
$browserinfo $_SERVER['HTTP_USER_AGENT'];
        
$sql "select userip from pageview where page=:link and userip='$user_ip'";
        
$stmt $conn->prepare($sql);
        
$stmt->bindParam(':link'$actual_linkPDO::PARAM_STR);
        
$stmt->execute();
        
$num_rows $stmt->fetchColumn();
        if(
$num_rows >= 1)
        {
            
$sql "update pageview set uservisits=uservisits+1,browserinfo=:browserinfo where page=:link and userip='$user_ip'";
            
$stmt $conn->prepare($sql);
            
$stmt->bindParam(':browserinfo'$browserinfoPDO::PARAM_STR);
            
$stmt->bindParam(':link'$actual_linkPDO::PARAM_STR);
            
$stmt->execute();
        }
        else
        {
            
$sql "insert into pageview values('',:link,'$user_ip','1',:browserinfo)";
            
$stmt $conn->prepare($sql);
            
$stmt->bindParam(':link'$actual_linkPDO::PARAM_STR);
            
$stmt->bindParam(':browserinfo'$browserinfoPDO::PARAM_STR);
            
$stmt->execute();
            
$sql "update totalview set totalvisit=totalvisit+1 where page=:link";
            
$stmt $conn->prepare($sql);
            
$stmt->bindParam(':link'$actual_linkPDO::PARAM_STR);
            
$stmt->execute();
            
$count $stmt->rowCount();
            if (
$count == 0)
            {
                
$sql "insert into totalview values('',:link,'1')";
                
$stmt $conn->prepare($sql);
                
$stmt->bindParam(':link'$actual_linkPDO::PARAM_STR);
                
$result $stmt->execute();
            }
        }
        
$sql "select totalvisit from totalview where page=:link";
        
$stmt $conn->prepare($sql);
        
$stmt->bindParam(':link'$actual_linkPDO::PARAM_STR);
        
$stmt->execute();
        
$result $stmt->fetch();
        return 
$result["totalvisit"];
    }
    catch (
Exception $pdoExe)
    {
        
$errors 'Caught exception: ' .$pdoExe->getMessage() . "\n";
        return 
"error";
    }
}

function 
counterStrava()
{
    if (isset(
$_GET['days']))
        
$days intval($_GET['days']);
    else
        
$days "ERR";
    if (
$days 999 || $days 0)
        
$days "ERR";
    else
        
$days str_pad($days3"0"STR_PAD_LEFT);

    if (isset(
$_GET['type']))
        
$type $_GET['type'];
    else
        
$type null;
    
    switch(
$type) {
        case 
"outside":
            
$type "outside.";
            break;
        
        case 
"inside":
            
$type "inside.";
            break;
            
        case 
"ebike":
            
$type "on an ebike.";
            break;
            
        default:
            
$type null;
            break;
    }
    
    if (
$type == null)
        
$im imagecreatetruecolor(205110);
    else
        
$im imagecreatetruecolor(205140);

    
$white imagecolorallocate($im255255255);
    
$grey imagecolorallocate($im128128128);
    
$black imagecolorallocate($im000);
    
$green imagecolorallocate($im5020550);
    
$pink imagecolorallocate($im255105180);
    
//imagefilledrectangle($im, 0, 0, 250, 250, $pink);
    
imagecolortransparent($im$white);
    
imagefilledrectangle($im00250250$white);

    
$font dirname(__FILE__).'/fonts/DSEG7Classic-Regular.ttf';
    
$font2 dirname(__FILE__).'/fonts/Ubuntu.ttf';

    
imagerectangle($im1113067$black);
    
imagettftext($im400261$black$font$days);
    
imagettftext($im400160$green$font$days);
    
imagettftext($im24013661$grey$font2"days");
    
imagettftext($im24013560$black$font2"days");
    
imagettftext($im2406101$grey$font2"since last ride");
    
imagettftext($im2405100$black$font2"since last ride");
    if (
$type != null)
    {
        
imagettftext($im2406136$grey$font2$type);
        
imagettftext($im2405135$black$font2$type);
    }
    
header('Content-type: image/png');
    
imagepng($im);
    
imagedestroy($im);
}

function 
getArchiveScriptJS($isStrava)
{
    
$output '<script>';
    if (
$isStrava)
    {
        
$output .= <<< "EOD"
        var d = new Date();
        var t = d.getTime();
        var photos = [];
        var photoLocations = [];
        function getStravaPhotos() {
            fetch("/strava_photos.json?"+t).then(response => {
                    response.json().then(data => {
                        data.forEach(photoUrl => {
                            if (photoUrl['location'] != null)
                                photoLocations.push(photoUrl);
                            if (photoUrl['url'].includes("768x358"))
                                photos.push(photoUrl['url']);
                        })
                    });
                }).catch(err => {
                    console.log(err);
            });
        }
        getStravaPhotos();
        /* CONTEXT MENU HAX */
        /*var stravaImg = document.querySelector("#slider");
        stravaImg.oncontextmenu = function(){
            alert("Hello! I am an alert box!!");
            return false;
        };*/
        var strava_container = document.querySelector(".containerwebcam");
        function getImageUrl() {
            var strava_image = document.querySelector(".image").src;
            strava_image = strava_image.replace('768x358','2048x956');
            window.open(strava_image, '_stravaimg');
        }
        
        var currentMarkers=[];
        var hideLocation = { lat: 46.14473116241679, lng: -112.8675197204895 };
        /* hide location but coords are here lol */
        function isNearHiddenPoint(checkPoint, centerPoint, km) {
            var ky = 40000 / 360;
            var kx = Math.cos(Math.PI * centerPoint.lat / 180.0) * ky;
            var dx = Math.abs(centerPoint.lng - checkPoint.lng) * kx;
            var dy = Math.abs(centerPoint.lat - checkPoint.lat) * ky;
            return Math.sqrt(dx * dx + dy * dy) <= km;
        }
        
        function showLocationMap() {
            var strava_image = document.querySelector(".image").src;
            if (currentMarkers!==null) {
                for (var i = currentMarkers.length - 1; i >= 0; i--) {
                    currentMarkers[i].remove();
                }
            }
            photoLocations.every(stPhotos => {
                if (stPhotos['url'] == strava_image && stPhotos['location'] != '') {
                    var imgLocation = { lat: stPhotos['location']['0'], lng: stPhotos['location']['1'] };
                    if (isNearHiddenPoint(imgLocation, hideLocation, .5))
                        return false;
                    var marker1 = new mapboxgl.Marker()
                        .setLngLat([stPhotos['location']['1'],stPhotos['location']['0']])
                        .addTo(map);
                    currentMarkers.push(marker1);
                    map.easeTo({center: [stPhotos['location']['1'],stPhotos['location']['0']]});
                    return false;
                }
                return true;
            });
        }
        strava_container.innerHTML += "<p style='margin-top: -5px; text-align: center'><a href='#fullimage' onclick='getImageUrl()'>full size image</a> | <a href='#location' onclick='showLocationMap()'>show location on map</a></p>";
        
EOD;
    }
    
$output .= <<< "EOD"
        currentLinks = document.querySelectorAll('a[href="'+window.location.search+'"]');
        /*if (currentLinks.length == 0) {
            currentLinks = document.querySelectorAll('a[href="?time=hourly"]');
        }*/
        currentLinks.forEach(function(link) {
            link.className += 'current-link'
        });
        
        function renderImages() {
            photos.forEach((image) => {
                sliderDom.innerHTML += "<img class='image' src='" + image + "' loading='lazy' />";
            });
        }
        
        function preloadImages() {
            if (preloadDom.innerHTML == '') {
                photos.forEach((image) => {
                    preloadDom.innerHTML += "<link rel='prefetch' as='image' href='" + image + "'/>";
                });
            }
        }

        function clearImages() {
            const images = document.getElementsByTagName("img");
            for (let i = 0; i < images.length; i++) {
                images[i].style.opacity = 0;
            }
        }

        function showImageOLD(image) {
            clearImages();
            document.getElementsByTagName("img")[image].style.opacity = 1;
            enablePrevNextButtons();
        }
        
        function showImage(imagenum) {
            var imageURL = photos[imagenum];
            if (imageURL == undefined)
            {
                console.log('delaying 250ms. waiting for json to load...');
                setTimeout(function() { showImage(imagenum); }, 250);
            } else {
                sliderImg.innerHTML = "<img class='image' src='" + imageURL + "' onload=\"javascript: preloadImages()\" loading='lazy' />";
                enablePrevNextButtons();
            }
        }

        function init() {
            showImage(currentImage);
            getSunTimes();
        }

        function getSunTimes() {
            var d = new Date();
            var t = d.getTime();
            fetch("/suntimes.json?"+t).then(response => {
                response.json().then(data => {
                    updateSunTimes(data[0]);
                })
            }).catch(err => {
                console.log(err);
            })
        }
        
        function updateSunTimes(data) {
            document.getElementById("sunrise").innerHTML = "Sunrise: " + data.sunrise;
            document.getElementById("sunset").innerHTML = "Sunset: " + data.sunset;
        }        
        
        function enablePrevNextButtons() {
            if(currentImage==0) {
                var elem = document.getElementById('prevButton');
                if(elem != null && elem != 'undefined')
                    document.getElementById("prevButton").style.opacity = 0;
                document.getElementById("prevoverlay").style.opacity = 0;
            } else {
                var elem = document.getElementById("prevButton");
                if(elem != null && elem != 'undefined')
                    document.getElementById("prevButton").style.opacity = 1;
                document.getElementById("prevoverlay").style.opacity = 1;
            }
            
            if(currentImage==photos.length-1) {
                var elem = document.getElementById("nextButton");
                if(elem != null && elem != 'undefined')
                    document.getElementById("nextButton").style.opacity = 0;
                document.getElementById("nextoverlay").style.opacity = 0;
            } else {
                var elem = document.getElementById("nextButton");
                if(elem != null && elem != 'undefined')
                    document.getElementById("nextButton").style.opacity = 1;
                document.getElementById("nextoverlay").style.opacity = 1;
            }
        }
        function myPrev() {
            if(currentImage==0) {
                return;
            }
            const newImage = (currentImage - 1);
            currentImage -= 1;
            showImage(newImage);
            if (typeof showLocationMap === "function")
                showLocationMap()
        }

        function myNext() {
            if(currentImage==photos.length-1) {
                return;
            }
            const newImage = (currentImage + 1);
            currentImage += 1;
            showImage(newImage);
            if (typeof showLocationMap === "function")
                showLocationMap();
        }
        

        /*init();*/
        /*window.onload = init();*/
        var sliderDom = document.getElementById("slider");
        var preloadDom = document.getElementById("preLoader");
        var sliderImg = document.querySelector("#sliderImage");
        var currentImage = 0;
        if (photos.length != 0)
            currentImage = photos.length-1;
        window.addEventListener("load",function(event) {
            init();
        },false);
    </script>
EOD;

    return 
$output;
}

function 
getArchivePageHTML()
{
    function 
getHourHTML()
    {
        
$output '';
        
$output .= <<< "EOD"
        
        <div class="hourStuff"><input type="time" id="picTime" name="picTime"></div>
        <script>
        const selectElement = document.querySelector('#picTime');
        selectElement.addEventListener('change', (event) => {
            const myHour = event.target.value.split(":");
            var searchParams = new URLSearchParams(window.location.search);
            if (searchParams.has('hour'))
            {
                searchParams.delete('hour');
                searchParams.append('hour', myHour[0]);
            }
            else
            {
                searchParams.append('hour', myHour[0]);
            }
            window.location = window.location.pathname + "?" + searchParams;
        });
        </script>
EOD;

        return 
$output;
    }
    function 
getCalendarHTML()
    {
        
$output '';
        
$output .= <<< "EOD"
        <div class="calendar"></div>
        <link rel="stylesheet" href="/archivephp/calendar.css">
        <script src="/archivephp/calendar.js"></script>    
EOD;

        return 
$output;
    }
    
$output '';
    
$output .= <<< "EOD"
    <div id="leftsidebar">
        <p><ol>TODO<li></li></ol></p>
    </div>
    <div id="content">
        <div class="containerwebcam">
            <div id="slider">
                <div id="sliderImage"></div>
                <div onclick="myPrev()" id="prevoverlay"><div class="middleleft"><div class="text">Previous</div></div></div>
                <div onclick="myNext()" id="nextoverlay"><div class="middleright"><div class="text">Next</div></div></div>
            </div>
            <div class="buttons">
                <button onclick="myPrev()" id="prevButton"><</button>
                <a href="?time=hourly">hourly</a> <a href="?time=daily&hour=12">daily</a> <a href="?time=monthly&hour=12">monthly</a>
                <button onclick="myNext()" id="nextButton">></button>
            </div>
            <div id="source">
                <span id="sunrise"></span> || <span id="sunset"></span>
            </div>
        </div>
        <div id="preLoader"></div>
        </div> <!-- #content -->
        <div id="sidebar">
EOD;

    if (
$_GET['time'] == "hourly" || $_GET['time'] == "")
    {
        
$output .= getCalendarHTML();
    }
    else
    {
        
$output .= getHourHTML();
    }

    
$output .= <<< "EOD"
    </div>
EOD;

    return 
$output;
}

function 
getAqiHTML()
{
    
$output '';
    
$output .= <<< "EOD"
    <div id="leftsidebar">
        <p><ol>TODO<li>cookie remember station id?</li></ol></p>
    </div>
    <div id="content">
        <div class="quick-links">
                <a href="#104446" onclick="getData(104446);return false;">Anaconda</a> || <a href="#105660" onclick="getData(105660);return false;">Butte</a> || <a href="https://www.purpleair.com/map?opt=1/mAQI/a10/cC0#8.93/45.9561/-112.564" target="_blank">PurpleAir Station ID #</a> <input id="pastation" type='text' onkeypress='validate(event)'><button onclick="getData(document.getElementById('pastation').value);return false;">Calculate</button>
        </div>
        <div class='aqi-container' id="containerPm25">
            <div class='station-id' id='station-id'></div>
            <div class='aqi-label'>AQI (PM2.5)</div>
            <div class='aqi' id="aqiPm25"></div>
            <div class='pm-label' id="pm25"></div>
        </div><br/>
        <div class="divTable">
            <div class="headRow">
                <div class="divCell">Exercise Length (hour)</div>
                <div class="divCell">Cigarettes smoked</div>
            </div>
            <div class="divRow">
                <div class="divCell">Resting (24hours)</div>
                <div id="idle" class="divCell"></div>
            </div>
            <div class="divRow">
                <div class="divCell">1</div>
                <div id="1hour" class="divCell"></div>
            </div>
            <div class="divRow">
                <div class="divCell">2</div>
                <div id="2hour" class="divCell"></div>
            </div>
            <div class="divRow">
                <div class="divCell">3</div>
                <div id="3hour" class="divCell"></div>
            </div>
            <div class="divRow">
                <div class="divCell">4</div>
                <div id="4hour" class="divCell"></div>
            </div>
            <div class="divRow">
                <div class="divCell">5</div>
                <div id="5hour" class="divCell"></div>
            </div>
        </div>
        <br/>
        <div id="info">
            <h4>How does this work?</h4>
            <p><b>Step 1</b>: I convert PM2.5 to AQI. That's the number of particles in the air that are a certain size. For this operation I used the same equation as <a href="https://www.airnow.gov/aqi/aqi-calculator/">the Air Now calculator</a>.</p>
            <p><b>Step 2</b>: based on <a href="http://berkeleyearth.org/archive/air-pollution-and-cigarette-equivalence/">this research</a>, the health impact of a particle concentration of 22ug/m3 per 24 hours is equivalent to about 1 cigarette. I divided the concentration from step 1 by 22 and 24, then multiplied by the number of hours exposed. This isn't going to be completely accurate since some kinds of air pollution are worse (wood smoke is apparently not as bad for you, for example) and I'm sure cigarettes vary in lethality. Note that I am looking at <i>impact to health</i>, not particles inhaled.</p>
            <p><b>Step 3</b>: based on <a href="https://www.bbc.co.uk/bitesize/guides/z3xq6fr/revision/2">lung volumes</a> during rest/exercise you consume different amounts of oxygen. So with 40 breaths per minute with an average volume of 3L/breath during vigorous exercise I come to the conclusion that you consume 20times (.5L x 12 breaths per minute vs 3L x 40 breaths per minute) more oxygen than your resting state.</p>
            <i>Is my logic off? Did I science wrong? Please <a href="mailto:" id="email">email</a> me your comments.</i>
            <script>
                var parts = ["@", "gmail", "com", ".", "dylix98"];
                var email = parts[4] + parts[0] + parts[1] + parts[3] + parts[2];
                document.getElementById("email").href += email;
            </script>
        </div>
        <br><br>
            
        <script type="text/javascript">
        if(window.location.hash) {
                purpleId = window.location.hash.substring(1);
                getData(purpleId);
        }
        function getData(station) {
            if (station == "") {
                console.log("value cannot be empty, idiot");
                return;
            }
            var d = new Date();
            var t = d.getTime();
            fetch("https://api.purpleair.com/v1/sensors/"+station, {
                headers: { 'X-API-Key': 'CCF6ADE0-0747-11EC-BAD6-42010A800017' }
            }).then(response => {
            response.json().then(data => {
                var pm25avg = (parseInt(data['sensor']['pm2.5_a']) + parseInt(data['sensor']['pm2.5_b'])) / 2;
                var dailyCig = pm25avg / 22;
                var hourlyCig = dailyCig / 24;
                var exerciseCig = hourlyCig * 20;
                var label = data['sensor']['name'];
                updateHtml(calcAQIpm25(pm25avg), pm25avg, dailyCig, exerciseCig, label);
                window.location.hash = station;
            })
            }).catch(err => {
                console.log(err);
            })
        }
        function updateHtml(aqiPm25, pm25, dailyCig, exerciseCig, label) {
            /* update HTML */
            document.getElementById("station-id").innerHTML = label;
            document.getElementById("aqiPm25").innerHTML = aqiPm25;
            document.getElementById("pm25").innerHTML = "(PM2.5: " + pm25 + " ug/m3)";

            /* set colors */
            colorsPm25 = getColor(aqiPm25);
            document.getElementById("containerPm25").style.background = colorsPm25.bg;
            document.getElementById("containerPm25").style.color = colorsPm25.text;
            
            /* set hours */
            document.getElementById("idle").innerHTML = dailyCig.toFixed("3");
            document.getElementById("1hour").innerHTML = exerciseCig.toFixed("3");
            document.getElementById("2hour").innerHTML = (exerciseCig*2).toFixed("3");
            document.getElementById("3hour").innerHTML = (exerciseCig*3).toFixed("3");
            document.getElementById("4hour").innerHTML = (exerciseCig*4).toFixed("3");
            document.getElementById("5hour").innerHTML = (exerciseCig*5).toFixed("3");
        }
        function calcAQIpm25(pm25) {
            let pm1 = 0;
            let pm2 = 12;
            let pm3 = 35.4;
            let pm4 = 55.4;
            let pm5 = 150.4;
            let pm6 = 250.4;
            let pm7 = 350.4;
            let pm8 = 500.4;

            let aqi1 = 0;
            let aqi2 = 50;
            let aqi3 = 100;
            let aqi4 = 150;
            let aqi5 = 200;
            let aqi6 = 300;
            let aqi7 = 400;
            let aqi8 = 500;

            let aqipm25 = 0;

            if (pm25 >= pm1 && pm25 <= pm2) {
                aqipm25 = ((aqi2 - aqi1) / (pm2 - pm1)) * (pm25 - pm1) + aqi1;
            } else if (pm25 >= pm2 && pm25 <= pm3) {
                aqipm25 = ((aqi3 - aqi2) / (pm3 - pm2)) * (pm25 - pm2) + aqi2;
            } else if (pm25 >= pm3 && pm25 <= pm4) {
                aqipm25 = ((aqi4 - aqi3) / (pm4 - pm3)) * (pm25 - pm3) + aqi3;
            } else if (pm25 >= pm4 && pm25 <= pm5) {
                aqipm25 = ((aqi5 - aqi4) / (pm5 - pm4)) * (pm25 - pm4) + aqi4;
            } else if (pm25 >= pm5 && pm25 <= pm6) {
                aqipm25 = ((aqi6 - aqi5) / (pm6 - pm5)) * (pm25 - pm5) + aqi5;
            } else if (pm25 >= pm6 && pm25 <= pm7) {
                aqipm25 = ((aqi7 - aqi6) / (pm7 - pm6)) * (pm25 - pm6) + aqi6;
            } else if (pm25 >= pm7 && pm25 <= pm8) {
                aqipm25 = ((aqi8 - aqi7) / (pm8 - pm7)) * (pm25 - pm7) + aqi7;
            }
            return aqipm25.toFixed(0);
        }
        function getColor(aqi) {
            switch (true) {
                case (aqi >= 50 && aqi < 100):
                    color = "yellow";
                    break;
                case (aqi >= 100 && aqi < 150):
                    color = "orange";
                    break;
                case (aqi >= 150 && aqi < 200):
                    color = "red";
                    break;
                case (aqi >= 200 && aqi < 300):
                    color = "purple";
                    break;
                case (aqi >= 300):
                    color = "brown";
                    break;
                default:
                    color = "Lime";
                    break;
            }
            return {bg: color, text: (aqi > 200) ? "white" : "black"};
        }
        function validate(evt) {
            var theEvent = evt || window.event;

            /* Handle paste */
            if (theEvent.type === 'paste') {
                key = event.clipboardData.getData('text/plain');
            } else {
            /* Handle key press */
                var key = theEvent.keyCode || theEvent.which;
                key = String.fromCharCode(key);
            }
            var regex = /[0-9]|\./;
            if( !regex.test(key) ) {
                theEvent.returnValue = false;
                if(theEvent.preventDefault) theEvent.preventDefault();
            }
        }
        function goTo(page, title, url) {
            if ("undefined" !== typeof history.pushState) {
                history.pushState({page: page}, title, url);
            } else {
                window.location.assign(url);
            }
        }

            var purpleId;
            if(window.location.hash) {
                purpleId = window.location.hash.substring(1);
            } else {
                purpleId = "104446";
            }
        </script>
    </div> <!--#content-->
    <div id="sidebar">
    </div> <!--#sidebar-->
EOD;
    return 
$output;
}

function 
getComicsHTML()
{
    
$output '';
    
$output .= <<< "EOD"
    <div id="leftsidebar">
        <p><ol>TODO<li>cookie remember name not number</li></ol></p>
    </div>
    <!--<link rel="stylesheet" href="/comics/reset.css">-->
    <script type="text/javascript" src="/comics/jquery-3.1.0.min.js"></script>
    <!--<script type="text/javascript" src="/backend/js/jquery.cookies.2.2.0.min.js"></script>-->
    <script type="text/javascript" src="/comics/jquery.cookie.js"></script>
    <script type="text/javascript" src="/comics/comics.js"></script>
    <div id="content">
        <div class="comics" id="comicstrip"><h4>Please select a comic from the list</h4>
        <p> &nbsp;&raquo; Use CTRL key plus left mouse click to select multiple comics strips.</p>
        <hr>
        <p> &nbsp;&raquo; Click Remember to setup a cookie to save the comics you wish to show up by default.</p>
        <hr>
        <p> &nbsp;&raquo; "Cyanide & Happiness" and xkcd added. (at bottom of list)</p>
        </div>
        <noscript><h2>I accidently the whole javascript.</h2></noscript>
    </div> <!--#content-->
    <div id="sidebar">
        <form action="" method="post">
        <select id="menu" multiple="multiple"></select>
        <input id="rememberme" type="button" value="Remember" class="button">
        <input id="clear" type="button" value="Clear" class="button">
        </form>
    </div> <!--#sidebar-->
EOD;
    return 
$output;
}

function 
getContactHTML($urlRequest)
{
    global 
$errors$successes;
    
$errors '';
    
$successes '';
    function 
getContactStyleCSS()
    {
        
$output '';
        
$output .= <<< "EOD"
        <style>
        div.elem-group {
            margin-bottom: 20px;
        }

        label {
            display: block;
            font-family: 'Aleo';
            padding-bottom: 4px;
            font-size: 1.00em;
        }

        input, select, textarea {
            border-radius: 2px;
            border: 1px solid #ccc;
            box-sizing: border-box;
            font-size: 1.00em;
            font-family: 'Aleo';
            width: 500px;
            padding: 8px;
        }

        textarea {
            height: 250px;
        }

        button {
            height: 50px;
            background: silver;
            color: black;
            margin-top: -10px;
            border: 2px solid gray;
            font-size: 1.00em;
            font-family: 'Aleo';
            border-radius: 4px;
            cursor: pointer;
        }

        button:hover {
            border: 2px solid black;
        }
        .lt{
            float: left;
        }
        .rt{
            float: right;
        }
        .fa-envelope, .gmail{
        display: inline-block;
        width: auto;
        }
        .list-item{
            list-style-type: none;
            margin: 0;
        }
        .list-item span{
            margin-left: 0;
            font-size: 1.05em;
        }
        
        </style>
EOD;
        return 
$output;
    }
    if(
$_SERVER["REQUEST_METHOD"] == "POST")
    {
        if (
filter_var($_POST['visitor_name'], FILTER_SANITIZE_STRING))
            
$visitorName $_POST['visitor_name'];
        else
        {
            
$visitorName $_POST['visitor_name'];
            
$errors .= "Name did not pass validation";
        }
        if (
filter_var($_POST['visitor_email'], FILTER_VALIDATE_EMAIL))
            
$visitorEmail $_POST['visitor_email'];
        else
        {
            
$visitorEmail $_POST['visitor_email'];
            
$errors .= "Email did not pass validation<br/>";
        }
        if (
filter_var($_POST['email_title'], FILTER_SANITIZE_STRING))
            
$emailTitle $_POST['email_title'];
        else
        {
            
$emailTitle $_POST['email_title'];
            
$errors .= "Reason to contact did not pass validation<br/>";
        }
        if (
filter_var($_POST['visitor_message'], FILTER_SANITIZE_STRING))
            
$visitorMessage $_POST['visitor_message'];
        else
        {
            
$visitorMessage $_POST['visitor_message'];
            
$errors .= "Message did not pass validation<br/>";
        }

        if(isset(
$_POST['captcha_challenge']) && $_POST['captcha_challenge'] == $_SESSION['captcha_text'] && $_SESSION['captcha_text'] != "") {
            
            if (isset(
$_POST['visitor_name']) && isset($_POST['visitor_email']) && isset($_POST['email_title']) && isset($_POST['visitor_message']))
            {
                if (
$_POST['visitor_name'] != "" && $_POST['visitor_email'] != "" && $_POST['email_title'] != "" && $_POST['visitor_message'] != "" && $errors == "")
                {
                    
$headers  "MIME-Version: 1.0\r\nContent-type: text/html; charset=utf-8\r\nFrom: contactform@dylix.org\r\nReply-To: $visitorEmail";
                    
$email_body ="<div>";
                    
$email_body .= "<div><label><b>Visitor Name:</b></label>&nbsp;<span>".$visitorName."</span></div>";
                    
$email_body .= "<div><label><b>Visitor Email:</b></label>&nbsp;<span>".$visitorEmail."</span></div>";
                    
$email_body .= "<div><label><b>Reason For Contacting:</b></label>&nbsp;<span>".$emailTitle."</span></div>";
                    
$email_body .= "<div><label><b>Visitor Message:</b></label><div>".$visitorMessage."</div></div>";
                    
$email_body .= "</div>";
                    if(
mail("dylix98@gmail.com"$emailTitle$email_body$headers)) {
                        
$successes "Thank you for contacting me, {$visitorName}. I typically reply within 24 hours.<br/>";
                        
$visitorName "";
                        
$visitorEmail "";
                        
$emailTitle "";
                        
$visitorMessage "";
                    }
                    else
                    {
                        
$errors "Sorry but the email did not go through.<br/>";
                    }
                }
            }
        }
        else
        {
            
$errors .="You entered an incorrect Captcha.<br/>";
        }
    }
    
$output '';
    
$output .= getHeaderHTML($urlRequest);
    
$output .= getContactStyleCSS();
    
$output .= <<< "EOD"
    <div id="leftsidebar">
    </div>
    <div id="content">
        <h4>Drop a Message</h4>
        <br/>
        <div class="lt2">
        <form action="/contact" method="post">
        <div class="elem-group">
            <label for="name">Your Name</label>
            <input type="text" id="name" name="visitor_name" placeholder="John Doe" pattern=[A-Z\sa-z]{3,60} required value="
{$visitorName}">
        </div>
        <div class="elem-group">
            <label for="email">Your E-mail</label>
            <input type="email" id="email" name="visitor_email" placeholder="john.doe@email.com" required value="
{$visitorEmail}">
        </div>
        <div class="elem-group">
            <label for="title">Reason For Contacting Me</label>
            <input type="text" id="title" name="email_title" required placeholder="How'd you do that" pattern=[A-Za-z0-9\s]{3,60} maxlength=60 value="
{$emailTitle}">
        </div>
        <div class="elem-group">
            <label for="message">Write your message</label>
            <textarea id="message" name="visitor_message" placeholder="I like your stuff!" required>
{$visitorMessage}</textarea>
        </div>
        <div class="elem-group">
            <label for="captcha">Please Enter the Captcha Text</label>
            <img style="width:200px; max-height: 50px; min-height:50px;" src="/captcha" alt="CAPTCHA" class="captcha-image" id="captcha-image"><span style="font-size: 48px; vertical-align: top;" id="refresh-captcha">&#x21bb;</span>
            <br>
            <input type="text" id="captcha" name="captcha_challenge" pattern="[A-Z]{6}" required>
        </div>
        <button type="submit">Send Message</button>
        </form>    
        </div>


    </div> <!--#content-->
    <div id="sidebar">
        <!-- Contact information -->
        <div class="rt2">
          <ul class="contact-list">
            <li class="list-item">
              <i class="fa fa-map-marker fa-1x">
                <span class="contact-text place">
                Christopher Hall<br/>
                85 Mertzig Rd.<br/>
                Anaconda, MT 59711
                </span>
             </i>
            </li>
            
            <li class="list-item">
              <i class="fa fa-phone fa-1x">
                <span id="contactphone"></span>
              </i>
            </li>
            
            <li class="list-item">
              <i class="fa fa-envelope fa-1x">
                <span class="contact-text gmail">
                    <a href="mailto:" id="contactemail">email</a>
                </span>
              </i>
            </li>
          </ul>
        </div>
        </div> <!--#contact>
    </div> <!--#sidebar-->
    <script>
        function doSecretStuffs() {
            var parts = ["@", "gmail", "com", ".", "dylix98"];
            var email = parts[4] + parts[0] + parts[1] + parts[3] + parts[2];
            var phParts = ["2351", "678", "314-"];
            var phone = "(" + phParts[1] + ") " + phParts[2] + phParts[0];
            document.getElementById("contactemail").href += email;
            document.getElementById("contactemail").innerHTML = email;
            document.getElementById("contactphone").innerHTML = phone;
        }
        var refreshButton = document.querySelector('#refresh-captcha');
        refreshButton.onclick = function() {
            document.querySelector("#captcha-image").src = '/captcha?' + Date.now();
        };
        window.addEventListener("load", (event) => {
            doSecretStuffs();
        });
    </script>
EOD;
    
$output .= getFooterHTML($urlRequest);
    return 
$output;
}

function 
getCopyrightHTML()
{
    
$copyright copyrightYears();
    
$output '';
    
$output .= <<< "EOD"
    <div id="leftsidebar">
    </div>
    <div id="content">
        <h4>© 
${copyright} dylix.org. All rights reserved.</h4>
        All files and information contained in this Website or Blog are copyright by dylix.org, and may not be duplicated, copied, modified or adapted, in any way without our written permission. Our Website or Blog may contain our service marks or trademarks as well as those of our affiliates or other companies, in the form of words, graphics, and logos. Your use of our Website, Blog or Services does not constitute any right or license for you to use our service marks or trademarks, without the prior written permission of dylix.org. Our Content, as found within our Website, Blog and Services, is protected under United States and foreign copyrights. The copying, redistribution, use or publication by you of any such Content, is strictly prohibited. Your use of our Website and Services does not grant you any ownership rights to our Content.
    </div> <!--#content-->
    <div id="sidebar">
    </div> <!--#sidebar-->
EOD;
    return 
$output;
}

function 
getCountHTML($urlRequest)
{
    global 
$errors;
    function 
getResults($results)
    {
        
//print_r($results);
        
$output '<table><thead><tr><th>IP</th><th>Page</th><th>Visits</th><th>Browser</th></thead><tbody>';
        foreach (
$results as $result) {
            
//$output .= "<tr><td>" . gethostbyaddr($result['userip']) . "</td><td>" . $result['page'] . "</td><td>" . $result['uservisits'] . "</td><td>" . $result['browserinfo'] . "</td></tr>";
            
$output .= "<tr><td>" $result['userip'] . "</td><td>" $result['page'] . "</td><td>" $result['uservisits'] . "</td><td>" $result['browserinfo'] . "</td></tr>";
        }
        
$output .= "</tbody></table>";
        return 
$output;
    }
    try
    {
        require 
__DIR__.'/counterdb.php';
        
$db_connection = new Database();
        
$conn $db_connection->dbConnection();
        
$user_ip $_SERVER['REMOTE_ADDR'];
        
$actual_link "/" $_GET['page']; //strtok($_SERVER['REQUEST_URI'], '?');
        
if ($actual_link == "/comics")
            
$actual_link "/comics/";
        elseif (
$actual_link == "/strava")
            
$actual_link "/strava/";

        
//$browserinfo = $_SERVER['HTTP_USER_AGENT'];
        //$sql = "select * from pageview where page=:link and userip='$user_ip'";
        
$sql "select * from pageview where page=:link";
        
$stmt $conn->prepare($sql);
        
$stmt->bindParam(':link'$actual_linkPDO::PARAM_STR);
        
$stmt->execute();
        
$result $stmt->fetchAll();
        
/*print_r($result);*/
        /*return $result["totalvisit"];*/
    
}
    catch (
Exception $pdoExe)
    {
        
$errors 'Caught exception: ' .$pdoExe->getMessage() . "\n";
        
/*return "error";*/
    
}
    
    
$output '';
    
$output .= getHeaderHTML($urlRequest);
    
$output .= <<< "EOD"
    <div id="leftsidebar">
    </div>
    <div id="content">
        <h4>Count</h4>
EOD;
    
    
$output .= getResults($result);
    
    
$output .= <<< "EOD"
    </div> <!--#content-->
    <div id="sidebar">
    </div> <!--#sidebar-->
EOD;
    
$output .= getFooterHTML($urlRequest);
    
    return 
$output;
}

function 
getDB2Json()
{
/*
SELECT ROUND(AVG(`hr`)) as AvgHR, ROUND(AVG(`rr`)) as AvgRR, DATE(`time`) as date FROM `heartrate` GROUP BY DATE


SELECT ROUND(AVG(`hr`)) as AvgHR, ROUND(AVG(`rr`)) as AvgRR
, DATE(`time`) as date, sec_to_time(time_to_sec(time)- time_to_sec(time)%(15*60)) as intervals from heartrate
group by intervals
*/
    
function doSQLJsonRequest($database$limit$startDate$endDate$avgs)
    {
        require 
__DIR__.'/jsondb.php';
        
$db_connection = new Database();
        
$conn $db_connection->dbConnection();
        
$data = [];
        if (
$avgs == "hourly") {
            if (
$database == "heartrate")
                
$sql "SELECT ROUND(AVG(hr)) AS hr, ROUND(AVG(rr)) AS rr, time FROM $database GROUP BY DATE_FORMAT(from_unixtime(unix_timestamp(time) - unix_timestamp(time) mod 3600), '%Y-%m-%d %H:%i')";
            elseif (
$database =="roomtemp")
                
$sql "SELECT ROUND(AVG(temp)) AS temp, ROUND(AVG(humidity)) AS humidity, time FROM $database GROUP BY DATE_FORMAT(from_unixtime(unix_timestamp(time) - unix_timestamp(time) mod 3600), '%Y-%m-%d %H:%i')";
            else
                
$sql "SELECT ROUND(AVG(pm25)) AS pm25, ROUND(AVG(pm10)) AS pm10, time FROM $database GROUP BY DATE_FORMAT(from_unixtime(unix_timestamp(time) - unix_timestamp(time) mod 3600), '%Y-%m-%d %H:%i')";
            
$stmt $conn->prepare($sql);
            
$stmt->execute();
            
$data $stmt->fetchAll(PDO::FETCH_ASSOC);
        }
        elseif (
$avgs == "daily") {
            if (
$database == "heartrate")
                
$sql "SELECT ROUND(AVG(`hr`)) as hr, ROUND(AVG(`rr`)) as rr, time FROM `$database` GROUP BY DATE(`time`)";
            elseif (
$database =="roomtemp")
                
$sql "SELECT ROUND(AVG(`temp`)) as temp, ROUND(AVG(`humidity`)) as humidity, time FROM `$database` GROUP BY DATE(`time`)";
            else
                
$sql "SELECT ROUND(AVG(`pm25`)) as pm25, ROUND(AVG(`pm10`)) as pm10, time FROM `$database` GROUP BY DATE(`time`)";
            
$stmt $conn->prepare($sql);
            
$stmt->execute();
            
$data $stmt->fetchAll(PDO::FETCH_ASSOC);
        }
        elseif (
$avgs == "weekly") {
            if (
$database == "heartrate")
                
$sql "SELECT ROUND(AVG(hr)) AS hr, ROUND(AVG(rr)) AS rr, time FROM $database GROUP BY WEEK(time), YEAR(time)";
            elseif (
$database =="roomtemp")
                
$sql "SELECT ROUND(AVG(temp)) AS temp, ROUND(AVG(humidity)) AS humidity, time FROM $database GROUP BY WEEK(time), YEAR(time)";
            else
                
$sql "SELECT ROUND(AVG(pm25)) AS pm25, ROUND(AVG(pm10)) AS pm10, time FROM $database GROUP BY WEEK(time), YEAR(time)";
            
$stmt $conn->prepare($sql);
            
$stmt->execute();
            
$data $stmt->fetchAll(PDO::FETCH_ASSOC);
        }
        elseif (
$startDate != '' && $endDate != '') {
            
$startDate .= " 00:00:00";
            
$endDate .= " 23:59:59";
            
$sql "SELECT * FROM ${database} WHERE time BETWEEN :startDate AND :endDate ORDER BY time DESC";
            
$stmt $conn->prepare($sql);
            
$stmt->bindParam(':startDate'$startDatePDO::PARAM_STR20);
            
$stmt->bindParam(':endDate'$endDatePDO::PARAM_STR20);
            
$stmt->execute();
            
//$stmt->debugDumpParams();
            
$data array_reverse($stmt->fetchAll(PDO::FETCH_ASSOC));
        } 
        else {
            
$sql "SELECT * FROM ${database} ORDER BY time DESC LIMIT :limit";
            
$stmt $conn->prepare($sql);
            
$stmt->bindParam(':limit'$limitPDO::PARAM_INT);
            
$stmt->execute();
            
$data array_reverse($stmt->fetchAll(PDO::FETCH_ASSOC));
        }
        return 
json_encode($data);
    }

    
$output '';
    
$dbRequest $_GET['db'];
    if (isset(
$_GET['limit']))
        
$limit $_GET['limit'];
    else
        
$limit 100;
    
    if (isset(
$_GET['avgs']))
        
$avgs $_GET['avgs'];
    else
        
$avgs '';
        
    
$startDate='';
    
$endDate='';
    if (isset(
$_GET['startDate']) && isset($_GET['endDate']))
    {
        
$startDate $_GET['startDate'];
        
$endDate $_GET['endDate'];
    }
    else if (isset(
$_GET['startDate']))
    {
        
$startDate $_GET['startDate'];
        
$endDate $_GET['startDate'];
    }
    
    switch(
$dbRequest) {
        case 
"heartrate":
            
$output .= doSQLJsonRequest($dbRequest$limit$startDate$endDate$avgs);
            break;
            
        case 
"roomtemp":
            
$output .= doSQLJsonRequest($dbRequest$limit$startDate$endDate$avgs);
            break;
            
        case 
"aqi":
            
$output .= doSQLJsonRequest($dbRequest$limit$startDate$endDate$avgs);
            break;
            
        default:
            
$output .= json_encode(jsonMSG(0,500,"Invalid DB selected"));
            break;
    }
    
header("Content-Type:application/json");
    return 
$output;
}

function 
getErrorHTML()
{
    global 
$errors;
    
$output '';
    if (
$errors=="")
        return 
$output;
    
    
$output .= <<< "EOD"
    <div id="errors">
        <h4>Error</h4>
        <span>
{$errors}</span>
    </div>
EOD;
    return 
$output;
}

function 
getFooterHTML($urlRequest)
{
    
$DateTime = new DateTime();
    
$DateTime->setTimestamp(filemtime(__FILE__))->modify('-7 hours'); /* i only care about my timezone */
    
$copyright copyrightYears();
    
$counter counter();
    
$errorHTML getErrorHTML();
    
$successHTML getSuccessHTML();
    
$output '';
    
$output .= <<< "EOD"
        </div> <!--#page-->
        <script>
        function setHeight()
        {
            if (document.getElementById("sidebar").offsetHeight > document.getElementById("leftsidebar").offsetHeight)
            {
                document.getElementById("leftsidebar").style.height = document.getElementById("sidebar").offsetHeight + "px";
            }
            else
            {
                document.getElementById("sidebar").style.height = document.getElementById("leftsidebar").offsetHeight + "px";
            }
        }
        window.addEventListener("load",function(event) {
            setHeight();
            setTimeout(function() { setHeight(); }, 1000);
            setTimeout(function() { setHeight(); }, 2000);
            setTimeout(function() { setHeight(); }, 3000);
            setTimeout(function() { setHeight(); }, 4000);
            setTimeout(function() { setHeight(); }, 5000);
            setTimeout(function() { setHeight(); }, 10000);
        },false);
        </script>
        
{$errorHTML}
        
{$successHTML}
        <div id="footer">
            <p class="left"><a href="/verified"><img style="min-height:16px; min-width: 16px; height: 16px; width: 16px; background: url() 50% no-repeat;" src="/images/verified.png"/></a> dylix.org © 
${copyright}. All rights reserved.</p>
            <p class="right">Modified: 
{$DateTime->format("F d Y h:i A")} | 
            <a href="/?dylix">source code</a> | 
            count: <a href="/count?page=
{$urlRequest}">{$counter}</a> | 
            <a href="/tos">Terms of Service</a> | 
            <a href="/privacypolicy">Privacy Policy</a> | 
            <a href="/copyright">Copyright</a> </p>
            <a rel="nofollow" style="display:none" href="https://dylix.org/blackhole/" title="Do NOT follow this link or you will be banned from the site!">dylix.org</a>
        </div> <!--#footer-->
    </body>
    </html>
EOD;

    return 
$output;
}

function 
getHeaderHTML($title)
{
    include(
realpath(getenv('DOCUMENT_ROOT')) . '/blackhole/index.php');
    
$output '';
    
$output .= <<< "EOD"
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>dylix.org | 
{$title}</title>
        <link rel="stylesheet" href="/style.css">
        <link rel='stylesheet' media='only screen and (min-width: 300px) and (max-width: 1000px)' href='/style-mobile.css' />
        <!--<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
        <meta http-equiv="Pragma" content="no-cache" />
        <meta http-equiv="Expires" content="0" />-->
    </head>
    <body>
        <div id="header">
            <header><a href="/">dylix.org</a> | <a href="/airquality">Air Quality</a> | <a href="/comics">Daily Comics</a> | <a href="/history">Historical Charts</a> | <a href="/strava">Strava</a> | <a href="/webcam">WebCam Archive</a> | <a href="/contact">Contact</a></header>
        </div>
        <div id="snow"></div>
        <div id="page">
EOD;

    return 
$output;
}

function 
getHistoryHTML()
{
    
$output '';
    
$output .= <<< "EOD"
    <div id="leftsidebar">
        <h4>HR History</h4>
        <div class="container">
            <p class='time' id="time"></p>
            <table>
                <thead>
                    <tr id="HRhistoryTableHeader">
                        <th>time</th>
                        <th>hr</th>
                        <th>rr int</th>
                    </tr>
                </thead>
                <tbody id="HRhistoryTable">
                </tbody>
            </table>
            <div class="link">
                <a href="javascript:void(0);" id="showHRPrevHistory">older</a> 
                <span id="HRcurrentRowsPage"></span> 
                <a href="javascript:void(0);" id="showHRNextHistory">newer</a>
            </div>
        </div>
    </div>
    <div id="content">
        <script src="/Chart-3.9.1.js"></script>
        <script src="/chartjs-adapter-date-fns.bundle.min.js"></script>
        <h4>HR Chart (<a href="javascript:getHRHistoryData(true);">load all</a>) (<a id="hrPrevDay">previous day</a>) (<a id="hrNextDay">next day</a>) (<a id="hrShowToday" href="javascript:void(0)">today</a>)<span style="float:right;">Results/Page:<select id="hrResultChange" onchange="hrResultChange()"><option value="25">25</option><option value="50">50</option><option value="100">100</option><option value="200">200</option><option value="500">500</option><option selected value="1000">1000</option></select></span><span style="float:right;">Avgs:<select id="hrAvgChange" onchange="hrAvgChange()"><option value="" selected></option><option value="hourly">hourly</option><option value="daily">daily</option><option value="weekly">weekly</option></select></span></h4>
        <script>
            let reqDate = new Date();
            const offset = reqDate.getTimezoneOffset();
            reqDate = new Date(reqDate.getTime() - (offset*60*1000));
            document.querySelector("#hrShowToday").href = "javascript:getHRHistoryData(\"" + reqDate.toISOString().split('T')[0] + "\");";
        </script>
        <canvas id="hrChart" style="width:100%;max-width:700px;height:200px;"></canvas>
        <div class="link">
            <a href="javascript:void(0);" id="showHRPrevHistory">older</a> 
            <span id="HRcurrentRowsPage"></span> 
            <a href="javascript:void(0);" id="showHRNextHistory">newer</a>
        </div>
        
        <h4>Room Temp Chart (<a href="javascript:getTempHistoryData(true);">load all</a>) (<a id="tempPrevDay">previous day</a>) (<a id="tempNextDay">next day</a>) (<a id="tempShowToday" href="javascript:void(0)">today</a>)<span style="float:right;">Results/Page:<select id="tempResultChange" onchange="tempResultChange()"><option value="25">25</option><option value="50">50</option><option value="100">100</option><option value="200">200</option><option value="500">500</option><option selected value="1000">1000</option></select></span><span style="float:right;">Avgs:<select id="tempAvgChange" onchange="tempAvgChange()"><option value="" selected></option><option value="hourly">hourly</option><option value="daily">daily</option><option value="weekly">weekly</option></select></span></h4>
        <script>
            document.querySelector("#tempShowToday").href = "javascript:getTempHistoryData(\"" + reqDate.toISOString().split('T')[0] + "\");";
        </script>
        <canvas id="tempChart" style="width:100%;max-width:700px;height:200px;"></canvas>
        <div class="link">
            <a href="javascript:void(0);" id="showTempPrevHistory">older</a> 
            <span id="TempcurrentRowsPage"></span> 
            <a href="javascript:void(0);" id="showTempNextHistory">newer</a>
        </div>
        
        <h4>Room AQI Chart (<a href="javascript:getHistoryData(true);">load all</a>) (<a id="aqiPrevDay">previous day</a>) (<a id="aqiNextDay">next day</a>) (<a id="aqiShowToday" href="javascript:void(0)">today</a>)<span style="float:right;">Results/Page:<select id="aqiResultChange" onchange="aqiResultChange()"><option value="25">25</option><option value="50">50</option><option value="100">100</option><option value="200">200</option><option value="500">500</option><option selected value="1000">1000</option></select></span><span style="float:right;">Avgs:<select id="aqiAvgChange" onchange="aqiAvgChange()"><option value="" selected></option><option value="hourly">hourly</option><option value="daily">daily</option><option value="weekly">weekly</option></select></span></h4>
        <script>
            document.querySelector("#aqiShowToday").href = "javascript:getHistoryData(\"" + reqDate.toISOString().split('T')[0] + "\");";
        </script>
        <canvas id="aqiChart" style="width:100%;max-width:700px;height:200px;"></canvas>
        <div class="link">
            <a href="javascript:void(0);" id="showPrevHistory">older</a> 
            <span id="currentRowsPage"></span> 
            <a href="javascript:void(0);" id="showNextHistory">newer</a>
        </div>
        
        <h4>Room Air Quality History</h4>
        <div class="container">
            <p class='time' id="time"></p>
            <table style="margin: auto;">
                <thead>
                    <tr id="historyTableHeader">
                        <th>time</th>
                        <th>pm2.5<br/>(µg/m³)</th>
                        <th>aqi<br/>(pm2.5)</th>
                        <th>pm10<br/>(µg/m³)</th>
                        <th>aqi<br/>(pm10)</th>
                    </tr>
                </thead>
                <tbody id="historyTable">
                </tbody>
            </table>
            <div class="link">
                <a href="javascript:void(0);" id="showPrevHistory">older</a> 
                <span id="currentRowsPage"></span> 
                <a href="javascript:void(0);" id="showNextHistory">newer</a>
            </div>
        </div> <!--#content-->
    </div>
    <div id="sidebar">
        <h4>Room Temp History</h4>
        <div class="container">
            <p class='time' id="time"></p>
            <table>
                <thead>
                    <tr id="TemphistoryTableHeader">
                        <th>time</th>
                        <th>temp</th>
                        <th>rh</th>
                    </tr>
                </thead>
                <tbody id="TemphistoryTable">
                </tbody>
            </table>
            <div class="link">
                <a href="javascript:void(0);" id="showTempPrevHistory">older</a> 
                <span id="TempcurrentRowsPage"></span> 
                <a href="javascript:void(0);" id="showTempNextHistory">newer</a>
            </div>
        </div>
    </div> <!--#sidebar-->
    <script type="text/javascript">
    function loadScript(scriptURL) {
        var head = document.getElementsByTagName('head')[0];
        var script = document.createElement('script');
        script.type = 'text/javascript';
        script.src = scriptURL + "?" + guid();
        head.appendChild(script);
    }
    function guid() {
        return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
        var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
        return v.toString(16);
        });
    }
    loadScript("/aqi/aqi.js");
    window.addEventListener("load", (event) => {
        getHistoryData(reqDate.toISOString().split('T')[0]);
        getHRHistoryData(reqDate.toISOString().split('T')[0]);
        getTempHistoryData(reqDate.toISOString().split('T')[0]);
    });
</script>
EOD;

    return 
$output;
}

function 
getJson2DB()
{
    function 
doBadRequest() {
        
$returnData jsonMSG(0,404,'Invalid Request');
        echo 
json_encode($returnData);
    }

    function 
doHR(&$conn, &$data) {
        
// IF REQUEST METHOD IS NOT POST
        
if($_SERVER["REQUEST_METHOD"] != "POST"):
            
$returnData jsonMSG(0,404,'dohr: not post');

        
// CHECKING EMPTY FIELDS
        
elseif(!isset($data->timestamp)
            || !isset(
$data->hr)
            || empty(
trim($data->timestamp))
            || empty(
trim($data->hr))
            ):
            
$fields = ['fields' => ['db','timestamp','hr','rr']];
            
$returnData msg(0,422,'Please Fill in all Required Fields!',$fields);

        
// IF THERE ARE NO EMPTY FIELDS THEN-
        
else:
            
$timestamp trim($data->timestamp);
            
$hr trim($data->hr);
            
$rr trim($data->rr);
            try{
                    
$insert_query "INSERT INTO `heartrate`(`time`,`hr`,`rr`) VALUES(:timestamp,:hr,:rr)";

                    
$insert_stmt $conn->prepare($insert_query);

                    
// DATA BINDING
                    
$insert_stmt->bindValue(':timestamp'$timestamp,PDO::PARAM_STR);
                    
$insert_stmt->bindValue(':hr'$hr,PDO::PARAM_INT);
                    
$insert_stmt->bindValue(':rr'$rr,PDO::PARAM_INT);

                    
$insert_stmt->execute();

                    
$returnData jsonMSG(1,201,'Data Added.');
            }
            catch(
PDOException $e){
                
$returnData jsonMSG(0,500,$e->getMessage());
            }
            
        endif;

        echo 
json_encode($returnData);
    }

    function 
doTemp(&$conn, &$data) {
        
// IF REQUEST METHOD IS NOT POST
        
if($_SERVER["REQUEST_METHOD"] != "POST"):
            
$returnData jsonMSG(0,404,'Invalid Request');

        
// CHECKING EMPTY FIELDS
        
elseif(!isset($data->timestamp
            || empty(
trim($data->timestamp))
            ):
            
$fields = ['fields' => ['db','timestamp','temp','humidity']];
            
$returnData jsonMSG(0,422,'Please Fill in all Required Fields!',$fields);

        
// IF THERE ARE NO EMPTY FIELDS THEN-
        
else:
            
$timestamp trim($data->timestamp);
            
$temp trim($data->temp);
            
$humidity trim($data->humidity);
            try{
                    
$insert_query "INSERT INTO `roomtemp`(`time`,`temp`,`humidity`) VALUES(:timestamp,:temp,:humidity)";

                    
$insert_stmt $conn->prepare($insert_query);

                    
// DATA BINDING
                    
$insert_stmt->bindValue(':timestamp'$timestamp,PDO::PARAM_STR);
                    
$insert_stmt->bindValue(':temp'$temp,PDO::PARAM_STR);
                    
$insert_stmt->bindValue(':humidity'$humidity,PDO::PARAM_STR);

                    
$insert_stmt->execute();

                    
$returnData jsonMSG(1,201,'Data Added.');
            }
            catch(
PDOException $e){
                
$returnData jsonMSG(0,500,$e->getMessage());
            }
            
        endif;

        echo 
json_encode($returnData);
    }

    function 
doAQI(&$conn, &$data) {
        
// IF REQUEST METHOD IS NOT POST
        
if($_SERVER["REQUEST_METHOD"] != "POST"):
            
$returnData jsonMSG(0,404,'doaqi: not post');

        
// CHECKING EMPTY FIELDS
        
elseif(!isset($data->timestamp
            || empty(
trim($data->timestamp))
            ):
            
$fields = ['fields' => ['timestamp','pm25','pm10']];
            
$returnData jsonMSG(0,422,'Please Fill in all Required Fields!',$fields);

        
// IF THERE ARE NO EMPTY FIELDS THEN-
        
else:
            
$timestamp trim($data->timestamp);
            
$pm25 trim($data->pm25);
            
$pm10 trim($data->pm10);
            try{
                    
$insert_query "INSERT INTO `aqi`(`time`,`pm25`,`pm10`) VALUES(:timestamp,:pm25,:pm10)";

                    
$insert_stmt $conn->prepare($insert_query);

                    
// DATA BINDING
                    
$insert_stmt->bindValue(':timestamp'$timestamp,PDO::PARAM_STR);
                    
$insert_stmt->bindValue(':pm25'$pm25,PDO::PARAM_STR);
                    
$insert_stmt->bindValue(':pm10'$pm10,PDO::PARAM_STR);

                    
$insert_stmt->execute();

                    
$returnData jsonMSG(1,201,'Data Added.');
            }
            catch(
PDOException $e){
                
$returnData jsonMSG(0,500,$e->getMessage());
            }
            
        endif;

        echo 
json_encode($returnData);
    }
    
    
// INCLUDING DATABASE AND MAKING OBJECT
    
require __DIR__.'/jsondb.php';
    
$db_connection = new Database();
    
$conn $db_connection->dbConnection();

    
// GET DATA FORM REQUEST
    
$data json_decode(file_get_contents("php://input"),false);
    
$returnData = [];
    
$requested_db $data->requested_db;
    
//echo "jsonerror:" . json_last_error() . " requesteddb: {$requested_db} phpinput:" . file_get_contents("php://input"); // why the fuck is this empty
    
switch($requested_db) {
        case 
"heart":
            
doHR($conn,$data);
            break;
        
        case 
"roomtemp":
            
doTemp($conn,$data);
            break;
            
        case 
"aqi":
            
doAQI($conn,$data);
            break;
            
        default:
            
doBadRequest();
            break;
    }
}

function 
getMainPageHTML()
{
    
$output '';
    
$output .= <<< "EOD"
    <div id="leftsidebar">
        <div id="strava"></div>
        <br/><a class="weatherwidget-io" href="https://forecast7.com/en/46d13n112d95/anaconda/?unit=us" data-label_1="ANACONDA" data-label_2="WEATHER" data-theme="original" >ANACONDA WEATHER</a>
        <script>
        !function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src='https://weatherwidget.io/js/widget.min.js';fjs.parentNode.insertBefore(js,fjs);}}(document,'script','weatherwidget-io-js');
        </script>
    </div>
    <div id="content">
        <h4>Webcam is updated from <span id="sunrise"></span> <span id="sunset"></span> <span style="float:right;" id="autorefresh"></span></h4>
        <img id="camera3" width=680>
        <h4>Photos from rides</h4>
        <div class="containerwebcam">
            <div class="strava" id="slider">
                <div id="sliderImage"></div>
                <div onclick="myPrev()" id="prevoverlay"><div class="middleleft"><div class="text">Previous</div></div></div>
                <div onclick="myNext()" id="nextoverlay"><div class="middleright"><div class="text">Next</div></div></div>
            </div>
            <!--<div class="buttons">
                <button onclick="myPrev()" id="prevButton"><</button>
                <button onclick="myNext()" id="nextButton">></button>
            </div>-->
        </div>
        <h4>Map of last few rides
            <span id="menu" style="float: right;">
                <input id="satellite-v9" type="radio" name="rtoggle" value="satellite"><label for="satellite-v9">satellite</label> | 
                <input id="light-v10" type="radio" name="rtoggle" value="light"><label for="light-v10">light</label> | 
                <input id="dark-v10" type="radio" name="rtoggle" value="dark"><label for="dark-v10">dark</label> | 
                <input id="streets-v11" type="radio" name="rtoggle" value="streets" checked="checked"><label for="streets-v11">streets</label> | 
                <input id="outdoors-v11" type="radio" name="rtoggle" value="outdoors"><label for="outdoors-v11">outdoors</label>
            </span>
        </h4>
        <script src='https://api.mapbox.com/mapbox-gl-js/v2.10.0/mapbox-gl.js'></script>
        <script src="/aqi/polyline.js"></script>
        <link href='https://api.mapbox.com/mapbox-gl-js/v2.10.0/mapbox-gl.css' rel='stylesheet' />
        <div id='map'></div>
        <script>
            mapboxgl.accessToken = 'pk.eyJ1IjoiZHlsaXgiLCJhIjoiY2w5dXNpbTViMGxjdzNucG1idjA1Y2d2aiJ9.mdaukvbgPewig5hsWiWO5w';
            const map = new mapboxgl.Map({
                container: 'map',
                style: 'mapbox://styles/mapbox/streets-v11',
                center: [-112.9478, 46.1264],
                zoom: 9,
                projection: 'globe'
            });
            /*map.on('style.load', () => {
                map.setFog({});
            });*/

            const layerList = document.getElementById('menu');
            const inputs = layerList.getElementsByTagName('input');
            for (const input of inputs) {
                input.onclick = (layer) => {
                    const layerId = layer.target.id;
                    map.setStyle('mapbox://styles/mapbox/' + layerId);
                    getStravaJson();
                    showLocationMap();
                };
            }
            
            function loadScript(scriptURL) {
                var head = document.getElementsByTagName('head')[0];
                var script = document.createElement('script');
                script.type = 'text/javascript';
                script.src = scriptURL + "?" + guid();
                head.appendChild(script);
            }
            function guid() {
                return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
                var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
                return v.toString(16);
                });
            }

            loadScript("/aqi/aqi.js");
            loadScript("/aqi/strava.js");
            /*loadScript("/aqi/snowflake.js");*/

            window.addEventListener("load",function(event) {
                getStravaJson();
                ChangeMedia();
                getData();
                setInterval(getData, 60000);
                getTemp();
                setInterval(getTemp, 60000);
                getHRM();
                setInterval(getHRM, 60000);
                getSunTimes();
                var reloadcam = setInterval(ChangeMedia, 60000);
                /*spawnSnowCSS(snowflakesCount);
                spawnSnow(snowflakesCount);*/
            },false);
        </script>    
    </div> <!--#content-->
    <div id="sidebar">
        <div class="container">
            <h1>Room AQI</h1>
            <div class='aqi-container' id="containerPm25">
                <div class='aqi-label'>AQI (PM2.5)</div>
                <div class='aqi' id="aqiPm25"></div>
                <div class='pm-label' id="pm25"></div>
            </div>
            <div class='aqi-container' id="containerPm10">
                <div class='aqi-label'>AQI (PM10)</div>
                <div class='aqi' id="aqiPm10"></div>
                <div class='pm-label' id="pm10"></div>
            </div>
            <div class="link">
                <a href="/history">history</a>
            </div>
            <div class='aqi-container' id="containerTemp">
                <div class='aqi-label'>Temperature / Humidity</div>
                <div class='aqi' id="temperature"></div>
                <div class='pm-label' id="humidity"></div>
            </div>
            <div class='aqi-container' id="containerHR">
                <div class='aqi-label'>Heart Rate</div>
                <div class='aqi' id="bpm"></div>
                <div class='pm-label' id="hrv"></div>
            </div>
            <div class='aqi-container' id="containerDATE">
            <div class='aqi-label'>Misc / Timestamps</div>
                <p class='time' id="avgHR"></p>
                <p class='time' id="timeHR"></p>
                <p class='time' id="timepm25"></p>
                <p class='time' id="timepm10"></p>
            </div>

            <!--<iframe title="Example 4" src="https://widget.airnow.gov/aq-dial-widget/?city=Anaconda&state=MT&country=USA&transparent=true" style="border: none; border-radius: 25px;"></iframe>-->
            <iframe title="Example Primary Pollutant" height="220" width="220" src="https://widget.airnow.gov/aq-dial-widget-primary-pollutant/?city=Anaconda&state=MT&country=USA" style="border: none; border-radius: 25px;"></iframe>

        </div>
    </div> <!--#sidebar-->
    <div id="preLoader"></div>
EOD;
    
$output .= getArchiveScriptJS(true);
    return 
$output;
}

function 
getNotFoundHTML()
{
    
$actual_domain = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' "https" "http") . "://$_SERVER[HTTP_HOST]";
    
$actual_link "$actual_domain$_SERVER[REQUEST_URI]";
    
$output '';
    
$output .= <<< "EOD"
    <div id="leftsidebar">
    </div>
    <div id="content">
        <div style="margin: auto; text-align: center;"><h4>These aren't the droids you're looking for</h4>
            <img src="/images/darthvader.png"/>
            <p>
{$actual_link} was not found.</p><br/>
            <button onclick="location.href='
{$actual_domain}';">Go Home</button>
        </div>
    </div> <!--#content-->
    <div id="sidebar">
    </div> <!--#sidebar-->
EOD;
    return 
$output;
}

function 
getPhotosDATA($path)
{
    global 
$errors$minFileDate;
    
$queryDate "";
    if(isset(
$_GET['time']))
    {
        
$imagetime $_GET['time'];
    }
    else
    {
        
$imagetime "hourly";
    }
    
$files array_diff(better_scandir($pathSCANDIR_SORT_ASCENDING), array('.''..''index.html'));
    
$first_date date('m/d/y',filemtime($path '/' .$files[0]));
    
$last_date date('m/d/y',filemtime($path '/' .$files[count($files)]));
    
$list "";
    
$listArray = [];
    
$minFileDate .= date('m/j/Y',filemtime($path '/' .$files[0]));
    
    if(isset(
$_GET['date']))
    {
        
$queryDate $_GET['date'];        
        
$now time();
        if(
strtotime($queryDate) > $now) {
             
$queryDate date("m/d/y");
             
$errors .= "Date is in the future.";
        }
        else
        {
            
$explodeDate explode("/",$queryDate);
            if (
$explodeDate[1] < 10)
            {
                
$explodeDate[1] = str_pad($explodeDate[1], 20STR_PAD_LEFT);
            }
            if (
$explodeDate[0] < 10)
            {
                
$explodeDate[0] = str_pad($explodeDate[0], 20STR_PAD_LEFT);
            }
            
$queryDate $explodeDate[0] . "/" $explodeDate[1] . "/" substr($explodeDate[2],2);
        }
    }
    else
    {
        
$queryDate date("m/d/y");
    }
    
    if (
$imagetime == "hourly")
    {
        foreach (
$files as $value)
        {
            if (
str_contains($valuedate('Y-m-d'strtotime($queryDate))))
            {
                
array_push($listArray'\'/archive/' $value '\'');
            }
        }
        if(
count($listArray)==0)
        {
            foreach (
$files as $value)
            {
                
$yesterday date("Y-m-d"strtotime("yesterday"));
                if (
str_contains($value$yesterday))
                {
                    
array_push($listArray'\'/archive/' $value '\'');
                }
            }
            
$formattedSame date("Y-m-d"strtotime($queryDate));
            
$errors .= "No images for {$formattedSame}. Falling back to " strval($yesterday) . "<br/>";
        }
    }
    else if (
$imagetime == "daily")
    {
        if(isset(
$_GET['hour']))
        {
            
$hour intval($_GET['hour']);
            if (!
in_range($hour522true))
            {
                
$hour 12;
                
$errors .= "valid range is 5 - 22";
            }
        }
        else
        {
            
$hour 12;
        }
        
$requestedHour $hour;
        
$currentDay $first_date;
        
        
dailyloop:
        
$hour str_pad($hour2'0'STR_PAD_LEFT);
        
$formattedFileName "'/archive/" strval(date('Y-m-d'strtotime($currentDay))) . "_" $hour ".jpg'";
        
$strippedFileName strval(date('Y-m-d'strtotime($currentDay))) . "_" $hour ".jpg";
        
/*echo "strippedFileName: {$strippedFileName}<br/>";
        echo "formattedFileName: {$formattedFileName}<br/>";
        echo "first_date: {$first_date}<br/>";
        echo "last_date: {$last_date}<br/>";
        echo "currentDay: {$currentDay}<br/>";
        echo "requestedHour: {$requestedHour}<br/><br/>";*/
        
        
if (in_array($strippedFileName,$files))
        {
            
array_push($listArray'\'/archive/' $strippedFileName '\'');
            
$currentDay date('m/d/y'strtotime('+1 day'strtotime($currentDay)));
            goto 
dailyloop;
        }
        else if ((
in_array(strval(date('Y-m-d'strtotime($currentDay))),$files) && $currentDay <= date('m/d/y')) || $currentDay == $last_date || date('Y-m-d'strtotime($currentDay)) == date('Y-m-d') || $currentDay $last_date)
        {
            
$errors .= "No images for {$currentDay} @ {$hour}. Skipping.<br/>";
            
$currentDay date('m/d/y'strtotime('+1 day'strtotime($currentDay)));
            goto 
dailyloop;
            
/*$hour -= 1;
            $errors = "No images for {$currentDay} @ {$_GET['hour']}. Going to a lower hour.";
            if ($hour < 24 && $hour > 0)
                goto dailyloop;*/
        
}
        
/*else if ($currentDay < $last_date)
        {
            $errors .= "No images for {$currentDay} @ {$_GET['hour']}. Skipping.<br/>";
            $currentDay = date('m/d/y', strtotime('+1 day', strtotime($currentDay)));
            goto dailyloop;
            /*$hour += 1;
            $errors = "No images for {$currentDay} @ {$_GET['hour']}. Going to a higher hour.";
            if ($hour < 24 && $hour > 0)
                goto dailyloop;*/
        /*}*/

        
if (count($listArray)==0)
        {
            
$errors "No images found. Resetting values<br/>";
            
$currentDay $first_date;
            
$hour 12;
            goto 
dailyloop;
        }
    }
    else
    {
        
// MONTHLY
        
if(isset($_GET['hour']))
        {
            
$hour intval($_GET['hour']);
            if (!
in_range($hour522true))
            {
                
$hour 12;
                
$errors .= "valid range is 5 - 22";
            }
        }
        else
        {
            
$hour 12;
        }
         
        
$currentMonth $first_date;
        
$loopcounter=0;
        
        
hourloop:
        
$hour str_pad($hour2'0'STR_PAD_LEFT);
        foreach (
$files as $value)
        {
            if (
str_contains($value$hour ".jpg") && str_contains($valuedate('Y-m-d'strtotime($currentMonth))))
            {
                if (
str_contains($valuedate('Y-m-d'strtotime($currentMonth))))
                {
                    
array_push($listArray'\'/archive/' $value '\'');
                    
$currentMonth date('m/d/y'strtotime('+1 month'strtotime($currentMonth)));
                }
            }
        }
        if (
count($listArray)==0)
        {
            
$hour -= 1;
            
$currentMonth $first_date;
            
$errors .= "<br/>Requested hour not available, switching to earliest available. ${hour}";
            if (
$hour 0# is endless recursion possible?
                
goto hourloop;
            else
            {
                
$currentMonth date('m/d/y'strtotime('-1 day'strtotime($currentMonth)));
                
$hour 12;
                
$errors .= "<br/>Going to next available day and resetting hour.";
                
$loopcounter += 1;
                if (
$loopcounter 50)
                    die(
"looped to death");
                goto 
hourloop;
            }
        }
    }
    return 
implode(',',$listArray);
}

function 
getPhotosJSON($path)
{
    global 
$minFileDate;
    
$output '';
    
$photosData getPhotosDATA($path);

    
$output .= <<< "EOD"
        <script>
        var photos = [ 
{$photosData} ];
        localStorage.setItem('minFileDate',"
{$minFileDate}");
        </script>
EOD;

    return 
$output;
}

function 
getPrivacyPolicyHTML()
{
    
$output '';
    
$output .= <<< "EOD"
    <div id="leftsidebar">
    </div>
    <div id="content">
    <style>
        my-email::after {
            content: attr(data-domain);
        }
        my-email::before {
            content: attr(data-user) "@";
        }
    </style>
        <h4>Website Privacy Policy</h4>
        <div id="contract" class="DCS">
            <div id="outputPage">
                <div data-exp="simple2" class="outputVersion1">
                    <div>
                        <p style="line-height:18.0pt;font-size:12.0pt;line-height:18.0pt;font-family:Times New Roman;color:#000000;text-align:Center;" class="documentTitle"><span style="font-style:normal;font-weight:bold;">dylix.org Privacy Policy</span>
                        </p>
                        <p style="line-height:18.0pt;font-size:12.0pt;line-height:18.0pt;font-family:Times New Roman;color:#000000;text-align:Center;">Type of website: Blog<br>Effective date: 1st day of November, 2022
                        </p>
                        <p style="line-height:18.0pt;font-size:12.0pt;line-height:18.0pt;font-family:Times New Roman;color:#000000;text-align:Left;">dylix.org (the "Site") is owned and operated by Christopher Hall. Christopher Hall is the data controller and can be contacted at: <br><my-email data-user="dylix98" data-domain="gmail.com"></my-email><br/><br/>
                        </p>
                        <p style="line-height:18.0pt;font-size:12.0pt;line-height:18.0pt;font-family:Times New Roman;color:#000000;text-align:Left;"><span style="font-style:normal;font-weight:bold;text-decoration:underline;">Purpose</span><br>The purpose of this privacy policy (this "Privacy Policy") is to inform users of our Site of the following: 
                        </p>
                        <ol start="1" style="line-height:18.0pt;font-size:12.0pt;line-height:18.0pt;font-family:Times New Roman;color:#000000;list-style:decimal;">
                            <li value="1"><span>The personal data we will collect;</span><span style="color:#000000;"><br></span>
                            </li>
                            <li value="2"><span>Use of collected data;</span><span style="color:#000000;"><br></span>
                            </li>
                            <li value="3"><span>Who has access to the data collected;</span><span style="color:#000000;"><br></span>
                            </li>
                            <li value="4"><span>The rights of Site users; and</span><span style="color:#000000;"><br></span>
                            </li>
                            <li value="5"><span>The Site's cookie policy.</span><span style="color:#000000;"><br></span>
                            </li>
                        </ol>
                        <p style="line-height:18.0pt;font-size:12.0pt;line-height:18.0pt;font-family:Times New Roman;color:#000000;text-align:Left;">This Privacy Policy applies in addition to the terms and conditions of our Site.
                        </p><div>
                        <p style="line-height:18.0pt;font-size:12.0pt;line-height:18.0pt;font-family:Times New Roman;color:#000000;text-align:Left;"><span style="font-style:normal;font-weight:bold;text-decoration:underline;">GDPR</span><br>For users in the European Union, we adhere to the Regulation (EU) 2016/679 of the European Parliament and of the Council of 27 April 2016, known as the General Data Protection Regulation (the "GDPR"). For users in the United Kingdom, we adhere to the GDPR as enshrined in the Data Protection Act 2018.
                        </p></div><div>
                        <p style="line-height:18.0pt;font-size:12.0pt;line-height:18.0pt;font-family:Times New Roman;color:#000000;text-align:Left;"><span style="font-style:normal;font-weight:bold;text-decoration:underline;">Consent</span><br>By using our Site users agree that they consent to:
                        </p>
                        <ol start="1" style="line-height:18.0pt;font-size:12.0pt;line-height:18.0pt;font-family:Times New Roman;color:#000000;list-style:decimal;">
                            <li value="1"><span>The conditions set out in this Privacy Policy.</span><span style="color:#000000;"><br></span>
                            </li>
                        </ol></div><div>
                        <p style="line-height:18.0pt;font-size:12.0pt;line-height:18.0pt;font-family:Times New Roman;color:#000000;text-align:Left;"><span style="font-style:normal;font-weight:bold;text-decoration:underline;">Legal Basis for Processing</span><br>We collect and process personal data about users in the EU only when we have a legal basis for doing so under Article 6 of the GDPR. <br><br>We rely on the following legal basis to collect and process the personal data of users in the EU:
                        </p>
                        <ol start="1" style="line-height:18.0pt;font-size:12.0pt;line-height:18.0pt;font-family:Times New Roman;color:#000000;list-style:decimal;">
                            <li value="1"><span>Processing of user personal data is necessary to a task carried out in the public interest or in the exercise of our official authority.</span><span style="color:#000000;"><br></span>
                            </li>
                        </ol></div>
                        <p style="line-height:18.0pt;font-size:12.0pt;line-height:18.0pt;font-family:Times New Roman;color:#000000;text-align:Left;"><span style="font-style:normal;font-weight:bold;text-decoration:underline;">Personal Data We Collect</span><br>We only collect data that helps us achieve the purpose set out in this Privacy Policy. We will not collect any additional data beyond the data listed below without notifying you first.<br><br>
                        </p>
                        <p style="line-height:18.0pt;font-size:12.0pt;line-height:18.0pt;font-family:Times New Roman;color:#000000;text-align:Left;"><span style="text-decoration:underline;">Data Collected Automatically</span><br>When you visit and use our Site, we may automatically collect and store the following information:
                        </p>
                        <ol start="1" style="line-height:18.0pt;font-size:12.0pt;line-height:18.0pt;font-family:Times New Roman;color:#000000;list-style:decimal;">
                            <li value="1"><span>IP address;</span><span style="color:#000000;"><br></span>
                            </li>
                            <li value="2"><span>Location;</span><span style="color:#000000;"><br></span>
                            </li>
                            <li value="3"><span>Hardware and software details;</span><span style="color:#000000;"><br></span>
                            </li>
                            <li value="4"><span>Clicked links; and</span><span style="color:#000000;"><br></span>
                            </li>
                            <li value="5"><span>Content viewed.</span><span style="color:#000000;"><br></span>
                            </li>
                        </ol>
                        <p style="line-height:18.0pt;font-size:12.0pt;line-height:18.0pt;font-family:Times New Roman;color:#000000;text-align:Left;"><span style="font-style:normal;font-weight:bold;text-decoration:underline;">How We Use Personal Data</span><br>Data collected on our Site will only be used for the purposes specified in this Privacy Policy or indicated on the relevant pages of our Site. We will not use your data beyond what we disclose in this Privacy Policy.<br><br>The data we collect automatically is used for the following purposes:
                        </p>
                        <ol start="1" style="line-height:18.0pt;font-size:12.0pt;line-height:18.0pt;font-family:Times New Roman;color:#000000;list-style:decimal;">
                            <li value="1"><span>Statistics.</span><span style="color:#000000;"><br></span>
                            </li>
                        </ol>
                        <p style="line-height:18.0pt;font-size:12.0pt;line-height:18.0pt;font-family:Times New Roman;color:#000000;text-align:Left;"><span style="font-style:normal;font-weight:bold;text-decoration:underline;">Who We Share Personal Data With</span><br><span style="text-decoration:underline;">Employees</span><br>We may disclose user data to any member of our organization who reasonably needs access to user data to achieve the purposes set out in this Privacy Policy.
                        </p><div>
                        <p style="line-height:18.0pt;font-size:12.0pt;line-height:18.0pt;font-family:Times New Roman;color:#000000;text-align:Left;"><span style="text-decoration:underline;">Other Disclosures</span><br>We will not sell or share your data with other third parties, except in the following cases:
                        </p>
                        <ol start="1" style="line-height:18.0pt;font-size:12.0pt;line-height:18.0pt;font-family:Times New Roman;color:#000000;list-style:decimal;">
                            <li value="1"><span>If the law requires it;</span><span style="color:#000000;"><br></span>
                            </li>
                            <li value="2"><span>If it is required for any legal proceeding;</span><span style="color:#000000;"><br></span>
                            </li>
                            <li value="3"><span>To prove or protect our legal rights; and</span><span style="color:#000000;"><br></span>
                            </li>
                            <li value="4"><span>To buyers or potential buyers of this company in the event that we seek to sell the company.</span><span style="color:#000000;"><br></span>
                            </li>
                        </ol>
                        <p style="line-height:18.0pt;font-size:12.0pt;line-height:18.0pt;font-family:Times New Roman;color:#000000;text-align:Left;">If you follow hyperlinks from our Site to another Site, please note that we are not responsible for and have no control over their privacy policies and practices.
                        </p></div>
                        <p style="line-height:18.0pt;font-size:12.0pt;line-height:18.0pt;font-family:Times New Roman;color:#000000;text-align:Left;"><span style="font-style:normal;font-weight:bold;text-decoration:underline;">How Long We Store Personal Data</span><br>User data will be stored until the purpose the data was collected for has been achieved.<br><br>You will be notified if your data is kept for longer than this period.
                        </p>
                        <p style="line-height:18.0pt;font-size:12.0pt;line-height:18.0pt;font-family:Times New Roman;color:#000000;text-align:Left;"><span style="font-style:normal;font-weight:bold;text-decoration:underline;">How We Protect Your Personal Data</span><br>Encrypted then burned in a firey fire<br><br>While we take all reasonable precautions to ensure that user data is secure and that users are protected, there always remains the risk of harm. The Internet as a whole can be insecure at times and therefore we are unable to guarantee the security of user data beyond what is reasonably practical.
                        </p>
                        <p style="line-height:18.0pt;font-size:12.0pt;line-height:18.0pt;font-family:Times New Roman;color:#000000;text-align:Left;"><span style="font-style:normal;font-weight:bold;text-decoration:underline;">Your Rights as a User</span><br>Under the GDPR, you have the following rights:
                        </p>
                        <ol start="1" style="line-height:18.0pt;font-size:12.0pt;line-height:18.0pt;font-family:Times New Roman;color:#000000;list-style:decimal;">
                            <li value="1"><span>Right to be informed;</span><span style="color:#000000;"><br></span>
                            </li>
                            <li value="2"><span>Right of access;</span><span style="color:#000000;"><br></span>
                            </li>
                            <li value="3"><span>Right to rectification;</span><span style="color:#000000;"><br></span>
                            </li>
                            <li value="4"><span>Right to erasure;</span><span style="color:#000000;"><br></span>
                            </li>
                            <li value="5"><span>Right to restrict processing;</span><span style="color:#000000;"><br></span>
                            </li>
                            <li value="6"><span>Right to data portability; and</span><span style="color:#000000;"><br></span>
                            </li>
                            <li value="7"><span>Right to object.</span><span style="color:#000000;"><br></span>
                            </li>
                        </ol><div>
                        <p style="line-height:18.0pt;font-size:12.0pt;line-height:18.0pt;font-family:Times New Roman;color:#000000;text-align:Left;"><span style="font-style:normal;font-weight:bold;text-decoration:underline;">Children</span><br>We do not knowingly collect or use personal data from children under 16 years of age. If we learn that we have collected personal data from a child under 16 years of age, the personal data will be deleted as soon as possible. If a child under 16 years of age has provided us with personal data their parent or guardian may contact our data protection officer.
                        </p></div>
                        <p style="line-height:18.0pt;font-size:12.0pt;line-height:18.0pt;font-family:Times New Roman;color:#000000;text-align:Left;"><span style="font-style:normal;font-weight:bold;text-decoration:underline;">How to Access, Modify, Delete, or Challenge the Data Collected</span><br>If you would like to know if we have collected your personal data, how we have used your personal data, if we have disclosed your personal data and to who we disclosed your personal data, if you would like your data to be deleted or modified in any way, or if you would like to exercise any of your other rights under the GDPR, please contact our data protection officer here:<br><br>Christopher Hall<br><my-email data-user="dylix98" data-domain="gmail.com"></my-email><br>
                        </p><div>
                        <p style="line-height:18.0pt;font-size:12.0pt;line-height:18.0pt;font-family:Times New Roman;color:#000000;text-align:Left;"><span style="font-style:normal;font-weight:bold;text-decoration:underline;">Cookie Policy</span><br>A cookie is a small file, stored on a user's hard drive by a website. Its purpose is to collect data relating to the user's browsing habits. You can choose to be notified each time a cookie is transmitted. You can also choose to disable cookies entirely in your internet browser, but this may decrease the quality of your user experience.
                        </p>
                        <p style="line-height:18.0pt;font-size:12.0pt;line-height:18.0pt;font-family:Times New Roman;color:#000000;text-align:Left;">We use the following types of cookies on our Site:
                        </p>
                        <ol start="1" style="line-height:18.0pt;font-size:12.0pt;line-height:18.0pt;font-family:Times New Roman;color:#000000;list-style:decimal;">
                            <li value="1"><span style="text-decoration:underline;">Functional cookies</span><br>Functional cookies are used to remember the selections you make on our Site so that your selections are saved for your next visits; and<span style="color:#000000;"><br></span>
                            </li>
                            <li value="2"><span style="text-decoration:underline;">Analytical cookies</span><br>Analytical cookies allow us to improve the design and functionality of our Site by collecting data on how you access our Site, for example data on the content you access, how long you stay on our Site, etc.<span style="color:#000000;"><br></span>
                            </li>
                        </ol></div>
                        <p style="line-height:18.0pt;font-size:12.0pt;line-height:18.0pt;font-family:Times New Roman;color:#000000;text-align:Left;"><span style="font-style:normal;font-weight:bold;text-decoration:underline;">Modifications</span><br>This Privacy Policy may be amended from time to time in order to maintain compliance with the law and to reflect any changes to our data collection process. When we amend this Privacy Policy we will update the "Effective Date" at the top of this Privacy Policy. We recommend that our users periodically review our Privacy Policy to ensure that they are notified of any updates. If necessary, we may notify users by email of changes to this Privacy Policy.
                        </p>
                        <p style="line-height:18.0pt;font-size:12.0pt;line-height:18.0pt;font-family:Times New Roman;color:#000000;text-align:Left;"><span style="font-style:normal;font-weight:bold;text-decoration:underline;">Complaints</span><br>If you have any complaints about how we process your personal data, please contact us through the contact methods listed in the <span style="font-style:italic;font-weight:normal;">Contact Information</span> section so that we can, where possible, resolve the issue. If you feel we have not addressed your concern in a satisfactory manner you may contact a supervisory authority. You also have the right to directly make a complaint to a supervisory authority. You can lodge a complaint with a supervisory authority by contacting the Information Commissioner's Office in the UK, Data Protection Commission in Ireland.
                        </p>
                        <p style="line-height:18.0pt;font-size:12.0pt;line-height:18.0pt;font-family:Times New Roman;color:#000000;text-align:Left;"><span style="font-style:normal;font-weight:bold;text-decoration:underline;">Contact Information</span><br>If you have any questions, concerns or complaints, you can contact our data protection officer, Christopher Hall, at:<br><my-email data-user="dylix98" data-domain="gmail.com"></my-email><br/><br/>
                        </p></div>
                </div>
            </div>
        </div>
    </div> <!--#content-->
    <div id="sidebar">
    </div> <!--#sidebar-->
EOD;
    return 
$output;
}

function 
getSleepForHTML()
{
    
$output '';
    
$sleepFor 5;
    if (
intval($_GET['seconds']))
    {
        
$sleepFor $_GET['seconds'];
    }
    
    
sleep($sleepFor);
    
$output .= "slept for {$sleepFor} seconds";
    
    
header("Content-Type: text/plain");
    return 
$output;
}

function 
getStravaHTML($urlRequest)
{
    function 
getMiles($meters) {
        return 
round($meters*0.000621371192,2);
    }

    function 
getFeet($meters) {
        return 
round($meters*3.2808,0);
    }

    function 
getMPH($meters) {
        return 
round($meters*2.23694,1);
    }
    
    function 
getFTemp($celcius) {
        if (
$celcius == "")
            return 
"NaN";
        return 
round(($celcius 9/5) + 32);
    }
    
    function 
getTimeReadable($seconds) {
        
$seconds round($seconds);
        return 
sprintf('%02d:%02d:%02d', ($seconds3600),($seconds60 60), $seconds60);
    }
    
    function 
parseJsonCounter()
    {
        
$json file_get_contents('strava_activity.json');
        
$stravaArray json_decode($jsontrue);
        
$output '';
        
        foreach(
$stravaArray as $activity)
        {
            if (
$activity == "Authorization Error")
                return 
$output "Authorization Error.<br/>Expired Token.";
            if (
$activity['type'] == "Ride")
            {

                
                
$datetime_activity = new DateTime($activity['start_date_local'] . " midnight");
                
$datetime_now = new DateTime('today midnight');
                
$difference $datetime_activity->diff($datetime_now);
                
$days $difference->d;
                
$id $activity['id'];
                
                
$output .= "<h3>Outside</h3><a href='https://strava.com/activities/$id'>" $activity['name'] . "</a>";
                
$output .= "<img src='/counterStrava?days={$days}&type=outside' style='background: url() 50% no-repeat;'/><br/><br/>";
                break;
            }
        }
        
        foreach(
$stravaArray as $activity)
        {
            if (
$activity['type'] == "VirtualRide")
            {

                
                
$datetime_activity = new DateTime($activity['start_date_local'] . " midnight");
                
$datetime_now = new DateTime('today midnight');
                
$difference $datetime_activity->diff($datetime_now);
                
$days $difference->d;
                
$id $activity['id'];
                
                
$output .= "<h3>Inside</h3><a href='https://strava.com/activities/$id'>" $activity['name'] . "</a>";
                
$output .= "<img src='/counterStrava?days={$days}&type=inside' style='background: url() 50% no-repeat;'/><br/><br/>";
                break;
            }
        }
        
        foreach(
$stravaArray as $activity)
        {
            if (
$activity['type'] == "EBikeRide")
            {

                
                
$datetime_activity = new DateTime($activity['start_date_local'] . " midnight");
                
$datetime_now = new DateTime('today midnight');
                
$difference $datetime_activity->diff($datetime_now);
                
$days $difference->d;
                
$id $activity['id'];
                
                
$output .= "<h3>E-Bike</h3><a href='https://strava.com/activities/$id'>" $activity['name'] . "</a>";
                
$output .= "<img src='/counterStrava?days={$days}&type=ebike' style='background: url() 50% no-repeat;'/>";
                break;
            }
        }

        return 
$output;
    }
    function 
parseJson(&$sidehrefs)
    {
        
$json file_get_contents('strava_history.json');
        
$stravaArray json_decode($jsontrue);
        
$output '';
        
$x 0;
        foreach (
array_reverse($stravaArray) as $year) {
            foreach(
$year as $activity) {
                
$x++;
                
$polyline addslashes($activity['map']['summary_polyline']);
                
$date date_create($activity['start_date_local']);
                
$date_title date_format($date'Y');
                
$date_human date_format($date'h:iA');
                
$distance getMiles($activity['distance']);
                
$elevation_gain getFeet($activity['total_elevation_gain']);
                
$speed getMPH($activity['average_speed']);
                
$watts round($activity['average_watts']);
                
$heartrate round($activity['average_heartrate']);
                
$temp getFTemp($activity['average_temp']);
                
$moving_time getTimeReadable($activity['moving_time']);
                
$elapsed_time getTimeReadable($activity['elapsed_time']);
                
$sidehrefs .= "<a href=\"#strava{$x}\">{$activity['name']}</a><br/>";
                
$output .= <<< "EOD"
        <a name="strava{$x}"></a>
        <div class="activity">
        <div >
        <h2>
{$activity['name']}</h2>
        <div id="
{$activity['map']['id']}" style="width: 400px; height: 400px; border-style: solid; align-items: center; justify-content: center; vertical-align:middle; text-align: center; margin: 0; display: flex;">no map available</div>
        </div>
        <div class="activity-data">
        <div style="padding: 5px; margin-bottom: 12px;"><b>
{$date_title}</b></div>
        <div class="icon-row" style="margin: auto auto 5px;">
{$activity['type']}</div>
        <table style="max-width: 190px; margin: auto;">
        <tbody>
        <tr><td style="width: 35px;">time</td><td style="width: 100px;">
{$date_human}</td></tr>
        <tr><td>distance</td><td>
{$distance} miles</td></tr>
        <tr><td>elevation</td><td>
{$elevation_gain} feet</td></tr>
        <tr><td>time</td><td><div>
{$moving_time}</div><div>{$elapsed_time}</div></td></tr>
        <tr><td>speed</td><td>
{$speed} mph</td></tr>
        <tr><td>watts</td><td>
{$watts}</td></tr>
        <tr><td>heartrate</td><td>
{$heartrate} bpm</td></tr>
        <tr><td>temp</td><td>
{$temp}°F</td></tr>
        </tbody>
        </table>
        <div class="activity-link"><a href="https://www.strava.com/activities/
{$activity['id']}" target="_blank" rel="noopener noreferrer">View on Strava</a></div></div></div><br/>
EOD;
        
        if (
$activity['start_latlng'][0] != 0) {
            
$output .= <<< "EOD"
            <script>
            var map = L.map('
{$activity['map']['id']}', { zoomControl:false }).setView([{$activity['start_latlng'][0]}{$activity['start_latlng'][1]}], 13);
            /*L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', { maxZoom: 19, attribution: '&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>' }).addTo(map);*/
            /*L.tileLayer('https://tiles.stadiamaps.com/tiles/alidade_smooth_dark/{z}/{x}/{y}{r}.png').addTo(map);*/
            L.tileLayer('https://tiles.stadiamaps.com/tiles/alidade_smooth_dark/{z}/{x}/{y}{r}.png',
                {
                  maxZoom: 20,
                  /*attribution: '&copy; <a href="https://stadiamaps.com/">StadiaMaps</a>, &copy; <a href="https://openmaptiles.org">OpenMapTiles</a>, &copy; <a href="http://openstreetmap.org">OpenStreetMap</a>'*/
                }
            ).addTo(map);
            var encoded = "
{$polyline}";
            var polyline = L.Polyline.fromEncoded(encoded).addTo(map);
            map.fitBounds(polyline.getBounds());
            /*map.attributionControl.setPrefix('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 9 6" width="20" height="10"><rect fill="#fff" width="9" height="3"/><rect fill="#d52b1e" y="3" width="9" height="3"/><rect fill="#0039a6" y="2" width="9" height="2"/></svg>Russia Rules');*/
            map.attributionControl.setPrefix('');
            map.touchZoom.disable();
            map.doubleClickZoom.disable();
            map.scrollWheelZoom.disable();
            map.boxZoom.disable();
            map.keyboard.disable();
            map.dragging.disable();
            </script>
EOD;
        }
                
            }
        }
        return 
$output;
    }
    
$sidehrefs '';
    
$todayDate date('F d'time());
    
$parsedJson parseJson($sidehrefs);
    
$parsedJsonCounter parseJsonCounter();
    
$output '';
    
$output .= getHeaderHTML($urlRequest);
    
$output .= '<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.2/dist/leaflet.css" integrity="sha256-sA+zWATbFveLLNqWO2gtiw3HL/lh1giY/Inf1BJ0z14=" crossorigin=""/>';
    
$output .= '<script src="https://unpkg.com/leaflet@1.9.2/dist/leaflet.js" integrity="sha256-o9N1jGDZrf5tS+Ft4gbIK7mYMipq9lqpVJ91xHSyKhg=" crossorigin=""></script>';
    
$output .= '<script src="/Polyline.encoded.js"></script>';
    
$output .= <<< "EOD"
    <div id="leftsidebar" style="position: -webkit-sticky; position: sticky; top: 0;">
    <script>
    let parent = document.querySelector('#leftsidebar').parentElement;

while (parent) {
    const hasOverflow = getComputedStyle(parent).overflow;
    if (hasOverflow !== 'visible') {
        console.log(hasOverflow, parent);
    }
    parent = parent.parentElement;
}
</script>
    <h5>Activities</h5>
    
{$sidehrefs}
    </div>
    <div id="content">
        <h4><b>Historical Activities from 
{$todayDate}</b></h4>
        
{$parsedJson}
    </div> <!--#content-->
    <div id="sidebar">
        
{$parsedJsonCounter}
    </div> <!--#sidebar-->
    <style>
    .activities-row {
    display: flex;
    overflow-x: auto;
    }

    .activity {
    padding: 10px;
    margin-right: 10px;
    display: flex;
    border: solid lightGrey;
    background: lightGrey;
    border-radius: 15px;
    }

    .activities-row .activity:first-child {
    margin-left: auto;
    }

    .activities-row .activity:last-child {
    margin-right: auto;
    }

    .icon {
    max-width: 40px;
    max-height: 20px;
    cursor: help;
    }

    .icon-row .icon {
    margin-left: 15px;
    }

    .activity-data {
    width: 190px;
    justify-content: center;
    position: relative;
    }

    .activity div table tbody tr td {
    padding-top: 7px;
    height: 25px;
    vertical-align: middle;
    }

    .activity-link {
    position: absolute;
    bottom: 0px;
    left: 25%;
    }

    .recenter {
    width: 30px;
    height: 30px;
    background: white;
    z-index: 800;
    position: relative;
    top: 12px;
    left: 255px;
    cursor: pointer;
    opacity: 0.5;
    border: solid #ccc 1px;
    border-radius: 2px;
    }

    .recenter:hover {
    opacity: 1;
    }

    .leaflet-control-zoom {
    opacity: 0.5;
    }

    .leaflet-control-zoom:hover {
    opacity: 1;
    }

    .activity-data {
    width: 300px;
    justify-content: center;
    position: relative;
    }

    .activity div table tbody tr td {
    padding-top: 2px;
    height: 10px;
    }

    .activity-link {
    position: relative;
    padding-top: 5px;
    left: 0;
    }
    }

    @media (max-width: 900px) {
    .recenter {
    opacity: 1;
    }

    .leaflet-control-zoom {
    opacity: 1;
    }
    }
</style>
EOD;
    
$output .= getFooterHTML($urlRequest);
    return 
$output;
}

function 
getSuccessHTML()
{
    global 
$successes;
    
$output '';
    if (
$successes=="")
        return 
$output;
    
    
$output .= <<< "EOD"
    <div id="successes">
        <h4>Success</h4>
        <span>
{$successes}</span>
    </div>
EOD;
    return 
$output;
}

function 
getTermsOfServiceHTML()
{
    
$output '';
    
$output .= <<< "EOD"
    <div id="leftsidebar">
    </div>
    <div id="content">
        <h4>Terms of Service</h4>
        <div id="outputPage">
            <div data-exp="simple2" class="outputVersion1">
                <div><div class=" header">
                <p style="line-height:18.0pt;font-size:12.0pt;line-height:18.0pt;font-family:Times New Roman;color:#000000;text-align:Left;">These terms and conditions (the "Terms and Conditions") govern the use of <span style="font-style:normal;font-weight:bold;">dylix.org</span> (the "Site"). This Site is owned and operated by Christopher Hall. This Site is a blog.<br><br>By using this Site, you indicate that you have read and understand these Terms and Conditions and agree to abide by them at all times.<br><br>THESE TERMS AND CONDITIONS CONTAIN A DISPUTE RESOLUTION CLAUSE THAT IMPACTS YOUR RIGHTS ABOUT HOW TO RESOLVE DISPUTES. PLEASE READ IT CAREFULLY.
                </p><div>
                <p style="line-height:18.0pt;font-size:12.0pt;line-height:18.0pt;font-family:Times New Roman;color:#000000;text-align:Left;"><span style="font-style:normal;font-weight:bold;text-decoration:underline;">Intellectual Property</span><br>All content published and made available on our Site is the property of Christopher Hall and the Site's creators. This includes, but is not limited to images, text, logos, documents, downloadable files and anything that contributes to the composition of our Site.
                </p></div><div>
                <p style="line-height:18.0pt;font-size:12.0pt;line-height:18.0pt;font-family:Times New Roman;color:#000000;text-align:Left;"><span style="font-style:normal;font-weight:bold;text-decoration:underline;">Acceptable Use</span><br>As a user of our Site, you agree to use our Site legally, not to use our Site for illegal purposes, and not to:
                </p>
                <ul style="line-height:18.0pt;font-size:12.0pt;line-height:18.0pt;font-family:Times New Roman;color:#000000;list-style:disc;">
                    <li style="margin-bottom:18.0pt;" value="1"><span>Harass or mistreat other users of our Site;</span><span style="color:#000000;"><br></span>
                    </li>
                    <li style="margin-bottom:18.0pt;" value="2"><span>Violate the rights of other users of our Site;</span><span style="color:#000000;"><br></span>
                    </li>
                    <li style="margin-bottom:18.0pt;" value="3"><span>Violate the intellectual property rights of the Site owners or any third party to the Site;</span><span style="color:#000000;"><br></span>
                    </li>
                    <li style="margin-bottom:18.0pt;" value="4"><span>Hack into the account of another user of the Site;</span><span style="color:#000000;"><br></span>
                    </li>
                    <li style="margin-bottom:18.0pt;" value="5"><span>Act in any way that could be considered fraudulent; or</span><span style="color:#000000;"><br></span>
                    </li>
                    <li style="margin-bottom:0.0pt;" value="6"><span>Post any material that may be deemed inappropriate or offensive.</span><span style="color:#000000;"><br></span>
                    </li>
                </ul>
                <p style="line-height:18.0pt;font-size:12.0pt;line-height:18.0pt;font-family:Times New Roman;color:#000000;text-align:Left;">If we believe you are using our Site illegally or in a manner that violates these Terms and Conditions, we reserve the right to limit, suspend or terminate your access to our Site. We also reserve the right to take any legal steps necessary to prevent you from accessing our Site.
                </p></div><div>
                <p style="line-height:18.0pt;font-size:12.0pt;line-height:18.0pt;font-family:Times New Roman;color:#000000;text-align:Left;"><span style="font-style:normal;font-weight:bold;text-decoration:underline;">Third Party Goods and Services</span><br>Our Site may offer goods and services from third parties. We cannot guarantee the quality or accuracy of goods and services made available by third parties on our Site.
                </p></div><div>
                <p style="line-height:18.0pt;font-size:12.0pt;line-height:18.0pt;font-family:Times New Roman;color:#000000;text-align:Left;"><span style="font-style:normal;font-weight:bold;text-decoration:underline;">Links to Other Websites</span><span style="text-decoration:underline;"><br></span>Our Site contains links to third party websites or services that we do not own or control. We are not responsible for the content, policies, or practices of any third party website or service linked to on our Site. It is your responsibility to read the terms and conditions and privacy policies of these third party websites before using these sites.
                </p></div><div>
                <p style="line-height:18.0pt;font-size:12.0pt;line-height:18.0pt;font-family:Times New Roman;color:#000000;text-align:Left;"><span style="font-style:normal;font-weight:bold;text-decoration:underline;">Limitation of Liability</span><br>Christopher Hall and our directors, officers, agents, employees, subsidiaries, and affiliates will not be liable for any actions, claims, losses, damages, liabilities and expenses including legal fees from your use of the Site.
                </p></div><div>
                <p style="line-height:18.0pt;font-size:12.0pt;line-height:18.0pt;font-family:Times New Roman;color:#000000;text-align:Left;"><span style="font-style:normal;font-weight:bold;text-decoration:underline;">Indemnity</span><br>Except where prohibited by law, by using this Site you indemnify and hold harmless Christopher Hall and our directors, officers, agents, employees, subsidiaries, and affiliates from any actions, claims, losses, damages, liabilities and expenses including legal fees arising out of your use of our Site or your violation of these Terms and Conditions.
                </p></div><div>
                <p style="line-height:18.0pt;font-size:12.0pt;line-height:18.0pt;font-family:Times New Roman;color:#000000;text-align:Left;"><span style="font-style:normal;font-weight:bold;text-decoration:underline;">Applicable Law</span><br>These Terms and Conditions are governed by the laws of the State of Montana.
                </p></div><div>
                <p style="line-height:18.0pt;font-size:12.0pt;line-height:18.0pt;font-family:Times New Roman;color:#000000;text-align:Left;"><span style="font-style:normal;font-weight:bold;text-decoration:underline;">Dispute Resolution</span><br>Subject to any exceptions specified in these Terms and Conditions, if you and Christopher Hall are unable to resolve any dispute through informal discussion, then you and Christopher Hall agree to submit the issue first before a non-binding mediator and to an arbitrator in the event that mediation fails. The decision of the arbitrator will be final and binding. Any mediator or arbitrator must be a neutral party acceptable to both you and Christopher Hall. The costs of any mediation or arbitration will be shared equally between you and Christopher Hall.
                </p>
                <p style="line-height:18.0pt;font-size:12.0pt;line-height:18.0pt;font-family:Times New Roman;color:#000000;text-align:Left;">Notwithstanding any other provision in these Terms and Conditions, you and Christopher Hall agree that you both retain the right to bring an action in small claims court and to bring an action for injunctive relief or intellectual property infringement. 
                </p></div><div>
                <p style="line-height:18.0pt;font-size:12.0pt;line-height:18.0pt;font-family:Times New Roman;color:#000000;text-align:Left;"><span style="font-style:normal;font-weight:bold;text-decoration:underline;">Severability</span><br>If at any time any of the provisions set forth in these Terms and Conditions are found to be inconsistent or invalid under applicable laws, those provisions will be deemed void and will be removed from these Terms and Conditions. All other provisions will not be affected by the removal and the rest of these Terms and Conditions will still be considered valid.
                </p></div><div>
                <p style="line-height:18.0pt;font-size:12.0pt;line-height:18.0pt;font-family:Times New Roman;color:#000000;text-align:Left;"><span style="font-style:normal;font-weight:bold;text-decoration:underline;">Changes</span><br>These Terms and Conditions may be amended from time to time in order to maintain compliance with the law and to reflect any changes to the way we operate our Site and the way we expect users to behave on our Site. We will notify users by email of changes to these Terms and Conditions or post a notice on our Site.
                </p></div><div>
                <p style="line-height:18.0pt;font-size:12.0pt;line-height:18.0pt;font-family:Times New Roman;color:#000000;text-align:Left;"><span style="font-style:normal;font-weight:bold;text-decoration:underline;">Contact Details</span><br>Please contact us if you have any questions or concerns. Our contact details are as follows:<br><my-email data-user="dylix98" data-domain="gmail.com"></my-email>
                </p>
                <p style="line-height:18.0pt;font-size:12.0pt;line-height:18.0pt;font-family:Times New Roman;color:#000000;text-align:Left;">You can also contact us through the feedback form available on our Site.
                </p></div>
                <p style="line-height:18.0pt;font-size:12.0pt;line-height:18.0pt;font-family:Times New Roman;color:#000000;text-align:Right;">Effective Date: 1st day of November, 2022
                </p></div>
            </div></div>
        </div>
    </div> <!--#content-->
    <div id="sidebar">
    </div> <!--#sidebar-->
EOD;
    return 
$output;
}

function 
getVerifiedHTML($urlRequest)
{
    
$output '';
    
$output .= getHeaderHTML($urlRequest);
    
$output .= <<< "EOD"
    <div id="leftsidebar">
    </div>
    <div id="content">
        <h4>100% Verified</h4>
        <img src="/images/verified.png" style="float:left; min-height: 24px; min-width: 24px; width: 24px; height: 24px;"/>
        This is a real website. You can tell that it is a real website, by the way that it is.</br>
        
<?xml version="1.0" encoding="UTF-8"?>
<svg version="1.1" viewBox="0,0,24,24" xmlns="http://www.w3.org/2000/svg">
<path d="M22.5 12.5c0-1.58-.875-2.95-2.148-3.6.154-.435.238-.905.238-1.4 0-2.21-1.71-3.998-3.818-3.998-.47 0-.92.084-1.336.25C14.818 2.415 13.51 1.5 12 1.5s-2.816.917-3.437 2.25c-.415-.165-.866-.25-1.336-.25-2.11 0-3.818 1.79-3.818 4 0 .494.083.964.237 1.4-1.272.65-2.147 2.018-2.147 3.6 0 1.495.782 2.798 1.942 3.486-.02.17-.032.34-.032.514 0 2.21 1.708 4 3.818 4 .47 0 .92-.086 1.335-.25.62 1.334 1.926 2.25 3.437 2.25 1.512 0 2.818-.916 3.437-2.25.415.163.865.248 1.336.248 2.11 0 3.818-1.79 3.818-4 0-.174-.012-.344-.033-.513 1.158-.687 1.943-1.99 1.943-3.484zm-6.616-3.334l-4.334 6.5c-.145.217-.382.334-.625.334-.143 0-.288-.04-.416-.126l-.115-.094-2.415-2.415c-.293-.293-.293-.768 0-1.06s.768-.294 1.06 0l1.77 1.767 3.825-5.74c.23-.345.696-.436 1.04-.207.346.23.44.696.21 1.04z" fill="#1da1f2"/>
</svg>
    </div> <!--#content-->
    <div id="sidebar">
    </div> <!--#sidebar-->
EOD;
    
$output .= getFooterHTML($urlRequest);
    return 
$output;
}

function 
better_scandir($dir$sorting_order SCANDIR_SORT_ASCENDING)
{
    
$files = array();

    foreach (
scandir($dir$sorting_order) as $file)
    {
        if (
$file[0] === '.')
        {
            continue;
        }

        
$files[$file] = filemtime($dir '/' $file);
    }

    if (
$sorting_order == SCANDIR_SORT_ASCENDING)
    {
        
asort($filesSORT_NUMERIC);
    }
    else
    {
        
arsort($filesSORT_NUMERIC);
    }

    
$ret array_keys($files);

    return (
$ret) ? $ret false;
}

if (!
function_exists('str_contains')) {
    function 
str_contains($haystack$needle) {
        return 
$needle !== '' && mb_strpos($haystack$needle) !== false;
    }
}

function 
jsonMSG($success,$status,$message,$extra = []){
    return 
array_merge([
        
'success' => $success,
        
'status' => $status,
        
'message' => $message]
    ,
$extra);
}

function 
in_range($number$min$max$inclusive FALSE)
{
    if (
is_int($number) && is_int($min) && is_int($max))
    {
        return 
$inclusive
            
? ($number >= $min && $number <= $max)
            : (
$number $min && $number $max) ;
    }
    return 
FALSE;
}

if(isset(
$_GET['dylix']))
{
    
highlight_file(__FILE__);
    die();
}

if(
true)

    
$urlRequest trim(str_replace('?'.$_SERVER['QUERY_STRING'], ''$_SERVER['REQUEST_URI']), '/');
    
session_start();
    
$output '';
    
    
/* echo "urlRequest: {$urlRequest} requestURI: {$_SERVER['REQUEST_URI']} queryString: {$_SERVER['QUERY_STRING']}";
    die(); */
    
    
if(!empty($urlRequest))
    {
        switch(
$urlRequest) {
            case 
"airquality":
                
$output .= getHeaderHTML($urlRequest);
                
$output .= getAqiHTML();
                
$output .= getFooterHTML($urlRequest);
                break;
            
            case 
"captcha":
                
$output .= captcha();
                break;
            
            case 
"counterStrava":
                
$output .= counterStrava();
                break;
            
            case 
"comics":
                
$output .= getHeaderHTML($urlRequest);    
                
$output .= getComicsHTML();
                
$output .= getFooterHTML($urlRequest);
                break;
            
            case 
"contact":
                
$output .= getContactHTML($urlRequest);
                break;
            
            case 
"copyright":
                
$output .= getHeaderHTML($urlRequest);
                
$output .= getCopyrightHTML();
                
$output .= getFooterHTML($urlRequest);
                break;
            
            case 
"count":
                
$output .= getCountHTML($urlRequest);
                break;
            
            case 
"db2json":
                
$output .= getDB2Json();
                break;
            
            case 
"history":
                
$output .= getHeaderHTML($urlRequest);
                
$output .= getHistoryHTML();
                
$output .= getFooterHTML($urlRequest);
                break;
            
            case 
"json2db":
                
$output .= getJson2DB();
                break;
            
            case 
"privacypolicy":
                
$output .= getHeaderHTML($urlRequest);
                
$output .= getPrivacyPolicyHTML();
                
$output .= getFooterHTML($urlRequest);
                break;
            
            case 
"sleepFor":
                
$output .= getSleepForHTML();
                break;
            
            case 
"strava":
                
$output .= getStravaHTML($urlRequest);
                break;
            
            case 
"tos":
                
$output .= getHeaderHTML($urlRequest);
                
$output .= getTermsOfServiceHTML();
                
$output .= getFooterHTML($urlRequest);
                break;
            
            case 
"verified":
                
$output .= getVerifiedHTML($urlRequest);
                break;
            
            case 
"webcam":
                
$HOME_PATH '/home/dyliywyp/public_html/archive';
                
$output .= getHeaderHTML($urlRequest);
                
$output .= getPhotosJSON($HOME_PATH);
                
$output .= getArchivePageHTML();
                
$output .= getArchiveScriptJS(false);
                
$output .= getFooterHTML($urlRequest);
                break;
            
            default:
                
$output .= getHeaderHTML($urlRequest);
                
$output .= getNotFoundHTML();
                
$output .= getFooterHTML($urlRequest);
                break;
        }
    }
    else
    {
        
$output .= getHeaderHTML("home");
        
$output .= getMainPageHTML();
        
$output .= getFooterHTML($urlRequest);
    }
    
    echo 
str_replace(["\t""\n"], ""$output);
    
//echo $output;
    //D(getPhotosJSON($HOME_PATH));
}