Skip to content

Instantly share code, notes, and snippets.

@RouNNdeL
Last active January 3, 2023 03:37
Show Gist options
  • Save RouNNdeL/e37b79542acb573ab9423afb9799e8dc to your computer and use it in GitHub Desktop.
Save RouNNdeL/e37b79542acb573ab9423afb9799e8dc to your computer and use it in GitHub Desktop.
Interpolated roll for Google's random number generator
// ==UserScript==
// @name Better Random Number
// @version 1.3
// @description Interpolated roll for Google's random number generator
// @author RouNdeL
// @match https://www.google.pl/search*
// @match https://www.google.com/search*
// @require http://code.jquery.com/jquery-3.3.1.min.js
// @resource customCSS https://home.zdul.xyz/material_select.css?v=1
// @grant GM_addStyle
// @grant GM_getResourceText
// ==/UserScript==
const newCSS = GM_getResourceText ("customCSS");
GM_addStyle (newCSS);
(function() {
'use strict';
const CLASS_JSON_TTL = 3600;
const COOKIE_NAME = "random_class";
const CLASS_ITEM_NAME = "classes";
const CLASS_JSON_URL = "https://home.zdul.xyz/random_classes.json";
const STRING_CLASS = "Klasa";
const STRING_NO_CLASS = "Brak";
const STRING_ROLL = "Losuj";
const STRING_ROLLING = "Losowanie...";
$(function(){
if($(".gws-csf-randomnumber__result").length === 0)
return;
const result = $(".gws-csf-randomnumber__result");
const min = $(".gws-csf-randomnumber__minVal");
const max = $(".gws-csf-randomnumber__maxVal");
let run_id = 0;
let current_class = parseInt(getCookie(COOKIE_NAME));
current_class = isNaN(current_class) ? -1 : current_class;
let classes = [];
let student = null;
(async function(){
classes = await fetchClasses();
const select_wrapper = createClassSelect(min, classes, STRING_CLASS, STRING_NO_CLASS);
const select = select_wrapper.find("select");
select_wrapper.css("margin-top", "24px");
select.val(current_class);
function applyCurrentClass() {
student = createStudentDiv(result, STRING_ROLL);
min.val(1)
max.val(classes[current_class]["students"].length);
}
select.change(function(e) {
current_class = parseInt($(e.target).val());
current_class = isNaN(current_class) ? -1 : current_class;
setCookie(COOKIE_NAME, current_class);
if(student !== null) destoryStudentDiv(result, student);
if(current_class !== -1) {
applyCurrentClass();
} else {
student = null;
}
});
if(current_class !== -1)
applyCurrentClass();
})();
let promise = null;
result.parent().parent().next().find("div[role=button]").click(function(e) {
const mx = parseInt(max.val());
const mn = parseInt(min.val());
run_id = Math.floor(Math.random() * 16);
roll(Math.floor(Math.random() * mx) + mn, run_id, STRING_ROLLING, STRING_ROLL);
return false;
});
async function roll(target, _run_id, rolling_string, roll_string, time = 750) {
let current = parseInt(result.text());
const steps = Math.abs(target - current);
const dir = current > target ? -1 : 1;
const increment = Math.ceil(steps*0.01) * dir;
let step = 0;
console.log(target);
while(current !== target && run_id === _run_id) {
// Try to optimize this check
if((current - target > increment && dir > 0) || (current - target < increment && dir < 0)) {
current = target;
} else {
current += increment;
}
result.text(current);
if(student !== null)
student.text(rolling_string);
const interval = interpolate(step+1, steps, time) - interpolate(step, steps, time);
step++;
await sleep(interval);
}
if(student !== null && run_id === _run_id) {
if(classes[current_class]["students"][current-1] !== undefined)
student.text(classes[current_class]["students"][current-1])
else
student.text(roll_string)
}
}
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
function interpolate(step, steps, time) {
let t = step/steps;
return (t * t * t * t * time);
}
});
async function fetchClasses(){
let item = JSON.parse(localStorage.getItem(CLASS_ITEM_NAME));
if(item === null || item.last_update < Date.now() - CLASS_JSON_TTL * 1000)
{
if(item === null) item = {};
item.classes = await new Promise(resolve => {
$.ajax(CLASS_JSON_URL, {dataType: "json"})
.done(json => {
resolve(json);
});
});
item.last_update = Date.now();
localStorage.setItem(CLASS_ITEM_NAME, JSON.stringify(item))
}
return item.classes;
}
function createStudentDiv(result_div, initial_value, insert = true) {
const font_size = result_div.css("font-size");
const student_div = result_div.clone();
student_div.css("font-size", `calc(${font_size}/2)`);
student_div.css("color", "#808080");
student_div.text(initial_value);
if(insert)
{
student_div.css("top", `calc(${result_div.css("top")} + ${font_size}/3 + 8px)`);
result_div.css("top", `calc(${result_div.css("top")} - ${font_size}/3 - 4px)`);
student_div.insertAfter(result_div);
}
return student_div;
}
function destoryStudentDiv(result_div, student_div, remove = true) {
if(remove) student_div.remove();
result_div.css("top", "50%");
}
function createClassSelect(min_or_max, class_list, class_string, none_string, insert = true) {
const parent = min_or_max.parent().parent();
const select_wrapper = $("<div>", {class: "select"});
const select = $("<select>", {class: "select-text", required: true});
select_wrapper.append(select);
select_wrapper.append($("<span>", {class: "select-highlight"}));
select_wrapper.append($("<span>", {class: "select-bar"}));
select_wrapper.append($("<label>", {class: "select-label", text: class_string}));
select.append($("<option>", {value: -1, text: none_string}));
for(let i = 0; i < class_list.length; i++)
{
select.append($("<option>", {value: i, text: class_list[i]["symbol"]}));
}
if(insert)
parent.append(select_wrapper);
return select_wrapper;
}
function setCookie(cname, cvalue, exdays = 30) {
var d = new Date();
d.setTime(d.getTime() + (exdays*24*60*60*1000));
var expires = "expires="+ d.toUTCString();
document.cookie = cname + "=" + cvalue + ";" + expires + ";path=/";
}
function getCookie(name) {
var nameEQ = name + "=";
var ca = document.cookie.split(';');
for(var i=0;i < ca.length;i++) {
var c = ca[i];
while (c.charAt(0)==' ') c = c.substring(1,c.length);
if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
}
return null;
}
})();
.select {
font-family:
'Roboto','Helvetica','Arial',sans-serif;
position: relative;
width: 100%;
}
.select-text {
position: relative;
font-family: inherit;
background-color: transparent;
width: 100%;
padding: 10px 10px 10px 0;
font-size: 18px;
border-radius: 0;
border: none;
border-bottom: 3px solid #aaaaaa;
}
/* Remove focus */
.select-text:focus {
outline: none;
border-bottom: 3px solid rgba(0,0,0, 0);
}
/* Use custom arrow */
.select .select-text {
appearance: none;
-webkit-appearance:none
}
.select:after {
position: absolute;
top: 18px;
right: 10px;
/* Styling the down arrow */
width: 0;
height: 0;
padding: 0;
content: '';
border-left: 6px solid transparent;
border-right: 6px solid transparent;
border-top: 6px solid rgba(0, 0, 0, 0.12);
pointer-events: none;
}
/* LABEL ======================================= */
.select-label {
color: rgba(0,0,0, 0.26);
font-size: 18px;
font-weight: normal;
position: absolute;
pointer-events: none;
left: 0;
top: 10px;
transition: 0.2s ease all;
}
/* active state */
.select-text:focus ~ .select-label, .select-text:valid ~ .select-label {
color: #aaaaaa;
top: -20px;
transition: 0.2s ease all;
font-size: 14px;
}
/* BOTTOM BARS ================================= */
.select-bar {
position: relative;
display: block;
width: 100%;
}
.select-bar:before, .select-bar:after {
content: '';
height: 2px;
width: 0;
bottom: 1px;
position: absolute;
background: #2F80ED;
transition: 0.2s ease all;
}
.select-bar:before {
left: 50%;
}
.select-bar:after {
right: 50%;
}
/* active state */
.select-text:focus ~ .select-bar:before, .select-text:focus ~ .select-bar:after {
width: 50%;
}
/* HIGHLIGHTER ================================== */
.select-highlight {
position: absolute;
height: 60%;
width: 100px;
top: 25%;
left: 0;
pointer-events: none;
opacity: 0.5;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment