Nono Timer

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

Tendrás que instalar una extensión para tu navegador como Tampermonkey, Greasemonkey o Violentmonkey si quieres utilizar este script.

You will need to install an extension such as Tampermonkey to install this script.

Tendrás que instalar una extensión como Tampermonkey o Violentmonkey para instalar este script.

Necesitarás instalar una extensión como Tampermonkey o Userscripts para instalar este script.

Tendrás que instalar una extensión como Tampermonkey antes de poder instalar este script.

Necesitarás instalar una extensión para administrar scripts de usuario si quieres instalar este script.

(Ya tengo un administrador de scripts de usuario, déjame instalarlo)

Tendrás que instalar una extensión como Stylus antes de poder instalar este script.

Tendrás que instalar una extensión como Stylus antes de poder instalar este script.

Tendrás que instalar una extensión como Stylus antes de poder instalar este script.

Para poder instalar esto tendrás que instalar primero una extensión de estilos de usuario.

Para poder instalar esto tendrás que instalar primero una extensión de estilos de usuario.

Para poder instalar esto tendrás que instalar primero una extensión de estilos de usuario.

(Ya tengo un administrador de estilos de usuario, déjame instalarlo)

// ==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",
});