Nono Timer

Adds a timer to Rosiminc's Nono Café with some basic functions.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey, Greasemonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да инсталирате разширение, като например Tampermonkey .

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Userscripts.

За да инсталирате скрипта, трябва да инсталирате разширение като Tampermonkey.

За да инсталирате този скрипт, трябва да имате инсталиран скриптов мениджър.

(Вече имам скриптов мениджър, искам да го инсталирам!)

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

(Вече имам инсталиран мениджър на стиловете, искам да го инсталирам!)

// ==UserScript==
// @name         Nono Timer
// @namespace    https://github.com/squall831/Nono-Timer
// @version      1.0.1
// @description  Adds a timer to Rosiminc's Nono Café with some basic functions.
// @author       squall831
// @match        https://rosiminc.github.io/sg-nonograms/*
// @icon         https://cdn.steamgifts.com/img/favicon.ico
// @grant        none
// @license      MIT
// ==/UserScript==


// Declare the variables for the timer display and the container of the timer manual controls.
let nonoTimer = document.createElement("h5");
nonoTimer.id = "display";
nonoTimer.innerHTML = "00:00:00";
document.querySelector("#nonoDiv").appendChild(nonoTimer);
const display = document.getElementById("display");

let timerControls = document.createElement("div");
timerControls.id = "timerControls";
document.querySelector("#msgDiv").insertAdjacentElement("beforebegin", timerControls);

// Next, the general variables for the timer.
let timer = null;
let startTime = 0;
let elapsedTime = 0;
let isRunning = false;
let timeId = 'time' + window.location.search.substr(4);
let savedTime = 0;

// Check if there is a saved time for the current Nono ID and put it on the timer display.
if (localStorage.getItem(timeId) != null) {
    savedTime = parseInt(localStorage.getItem(timeId));

    let hours = Math.floor(savedTime / (1000 * 60 * 60));
    let minutes = Math.floor(savedTime / (1000 * 60) % 60);
    let seconds = Math.floor(savedTime / 1000 % 60);

    hours = String(hours).padStart(2, "0");
    minutes = String(minutes).padStart(2, "0");
    seconds = String(seconds).padStart(2, "0");

    display.textContent = `${hours}:${minutes}:${seconds}`;
};

// These are the functions to start, stop, and reset the timer. Update rate is set to each 100ms.
function start(){
    if (document.querySelector("#msgDiv").style.display != 'block') {
        if(!isRunning){
            startTime = Date.now() - elapsedTime - savedTime;
            timer = setInterval(update, 100);
            isRunning = true;
        };
    };
};

function stop(){
    if(isRunning){
        clearInterval(timer);
        elapsedTime = Date.now() - startTime - savedTime;
        isRunning = false;
    }
};

function reset(){
    clearInterval(timer);
    startTime = 0;
    elapsedTime = 0;
    isRunning = false;
    display.textContent = "00:00:00";
    localStorage.removeItem(timeId);
    savedTime = 0;
};

// This update function will keep updating the timer display once called ear 250ms and will stop
// automatically once the Nono is solved.
function update(){
    const currentTime = Date.now();

    elapsedTime = currentTime - startTime;
    localStorage.setItem(timeId, elapsedTime);

    let hours = Math.floor(elapsedTime / (1000 * 60 * 60));
    let minutes = Math.floor(elapsedTime / (1000 * 60) % 60);
    let seconds = Math.floor(elapsedTime / 1000 % 60);

    hours = String(hours).padStart(2, "0");
    minutes = String(minutes).padStart(2, "0");
    seconds = String(seconds).padStart(2, "0");

    display.textContent = `${hours}:${minutes}:${seconds}`;

    if (document.querySelector("#msgDiv").style.display == 'block') {
	stop();
    }
};

// These are the proper buttons for the timer's  manual start and stop.
let timerStart = document.createElement("button");
timerStart.innerHTML = "Start";
timerStart.type = "button";
timerStart.className = "btn btn-primary";
timerStart.id = "timerStart";
timerStart.addEventListener("click", start);

let timerStop = document.createElement("button");
timerStop.innerHTML = "Stop";
timerStop.type = "button";
timerStop.className = "btn btn-primary";
timerStop.id = "timerStop";
timerStop.addEventListener("click", stop);

// This is the placement of the timer display and buttons on the site.
document.querySelector("#nonoDiv").insertAdjacentElement("afterend", nonoTimer);
document.querySelector("#timerControls").appendChild(timerStart);
document.querySelector("#timerControls").appendChild(timerStop);

// We assing the reset function to the Reset button for the Nono to also reset the timer.
// Also assing the start function once the Nono is clicked.
document.querySelector("#resetBtn").addEventListener("click", reset);
document.querySelector("#nonoDiv").addEventListener("click", start);
document.querySelector("#nonoDiv").addEventListener("contextmenu", start);

// These functions allow for the timer to stop once we change tabs on the browser or use
// another program, and then we resume once we focus the tab again.
window.addEventListener('blur', stop);
window.addEventListener('focus', function() {
    if (display.textContent != "00:00:00") {
        start();
    };
});


// This function allows us to insert custom CSS into the page, using it to style the buttons.
let addRule = (function (style) {
    var sheet = document.head.appendChild(style).sheet;
    return function (selector, css) {
        var propText = typeof css === "string" ? css : Object.keys(css).map(function (p) {
            return p + ":" + (p === "content" ? "'" + css[p] + "'" : css[p]);
        }).join(";");
        sheet.insertRule(selector + "{" + propText + "}", sheet.cssRules.length);
    };
})(document.createElement("style"));

addRule("#timerControls", {
    "display": "flex",
    "margin-bottom": ".5rem!important",
    "gap": ".3rem!important",
});

addRule("#timerStart", {
    "--bs-btn-bg": "#006600",
    "--bs-btn-border-color": "#006600",
    "--bs-btn-hover-bg": "#084908",
    "--bs-btn-hover-border-color": "#084908",
});

addRule("#timerStop", {
    "--bs-btn-bg": "#c04f15",
    "--bs-btn-border-color": "#c04f15",
    "--bs-btn-hover-bg": "#8c441e",
    "--bs-btn-hover-border-color": "#8c441e",
});