Last active
January 3, 2023 03:37
-
-
Save RouNNdeL/e37b79542acb573ab9423afb9799e8dc to your computer and use it in GitHub Desktop.
Interpolated roll for Google's random number generator
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==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; | |
} | |
})(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
.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