From d581e5b61a771ef8619a5bfbc84a6e337c7ca13f Mon Sep 17 00:00:00 2001 From: Terry Truong Date: Sat, 21 Jan 2023 14:08:48 +1100 Subject: Move general utility funcs into util.ts --- src/lib.ts | 133 ++----------------------------------------------------------- 1 file changed, 4 insertions(+), 129 deletions(-) (limited to 'src/lib.ts') diff --git a/src/lib.ts b/src/lib.ts index 7c65d6b..e8ed448 100644 --- a/src/lib.ts +++ b/src/lib.ts @@ -1,134 +1,12 @@ /* - * Project-wide globals + * Common project globals */ +import {moduloPositive, intToOrdinal, getNumTrailingZeros} from './util'; import {RBTree} from './rbtree'; export const DEBUG = true; -// ========== For device detection ========== - -export type Breakpoint = 'sm' | 'md' | 'lg'; - -export function getBreakpoint(): Breakpoint { - const w = window.innerWidth; - if (w < 768){ - return 'sm'; - } else if (w < 1024){ - return 'md'; - } else { - return 'lg'; - } -} - -// Returns true for a touch device -export function onTouchDevice(){ - return window.matchMedia('(pointer: coarse)').matches; -} - -// For detecting writing mode - // Used with ResizeObserver callbacks, to determine which resized dimensions are width and height -export let WRITING_MODE_HORZ = true; - -if ('writing-mode' in window.getComputedStyle(document.body)){ // Can be null when testing - const bodyStyles = window.getComputedStyle(document.body); - if ('writing-mode' in bodyStyles){ - WRITING_MODE_HORZ = (bodyStyles['writing-mode'] as string).startsWith('horizontal'); - } -} - -// ========== For handler throttling ========== - -// For creating throttled version of handler function -export function makeThrottled(hdlr: (...args: any[]) => void, delay: number){ - let timeout = 0; - return (...args: any[]) => { - clearTimeout(timeout); - timeout = window.setTimeout(async () => hdlr(...args), delay); - }; -} -// Like makeThrottled(), but accepts an async function -export function makeThrottledAsync(hdlr: (...args: any[]) => Promise, delay: number){ - let timeout = 0; - return async (...args: any[]) => { - clearTimeout(timeout); - timeout = window.setTimeout(async () => await hdlr(...args), delay); - }; -} -// Like makeThrottled(), but, for runs of fast handler calls, calls it at spaced intervals, and at the start/end -export function makeThrottledSpaced(hdlr: (...args: any[]) => void, delay: number){ - let lastHdlrTime = 0; // Used for throttling - let endHdlr = 0; // Used to call handler after ending a run of calls - return (...args: any[]) => { - clearTimeout(endHdlr); - const currentTime = new Date().getTime(); - if (currentTime - lastHdlrTime > delay){ - lastHdlrTime = currentTime; - hdlr(...args); - lastHdlrTime = new Date().getTime(); - } else { - endHdlr = window.setTimeout(async () => { - endHdlr = 0; - hdlr(...args); - lastHdlrTime = new Date().getTime(); - }, delay); - } - }; -} - -// ========== General utility functions ========== - -// Similar to %, but for negative LHS, return a positive offset from a lower RHS multiple -export function moduloPositive(x: number, y: number){ - return x - Math.floor(x / y) * y; -} - -// For positive int n, converts 1 to '1st', 2 to '2nd', etc -export function intToOrdinal(n: number){ - if (n == 1 || n > 20 && n % 10 == 1){ - return `${n == 1 ? '' : Math.floor(n / 10)}1st`; - } else if (n == 2 || n > 20 && n % 10 == 2){ - return `${n == 2 ? '' : Math.floor(n / 10)}2nd`; - } else if (n == 3 || n > 20 && n % 10 == 3){ - return `${n == 3 ? '' : Math.floor(n / 10)}3rd`; - } else { - return String(n) + 'th'; - } -} - -// For positive int n, returns number of trailing zeros in decimal representation -export function getNumTrailingZeros(n: number): number { - let pow10 = 10; - while (pow10 != Infinity){ - if (n % pow10 != 0){ - return Math.log10(pow10 / 10); - } - pow10 *= 10; - } - throw new Error('Exceeded floating point precision'); -} - -// Removes a class from an element, triggers reflow, then adds the class -export function animateWithClass(el: HTMLElement, className: string){ - el.classList.remove(className); - el.offsetWidth; // Triggers reflow - el.classList.add(className); -} - -// Used to async-await for until after a timeout -export async function timeout(ms: number){ - return new Promise(resolve => setTimeout(resolve, ms)) -} - -// For estimating text width (via https://stackoverflow.com/questions/118241/calculate-text-width-with-javascript) -const _getTextWidthCanvas = document.createElement('canvas'); -export function getTextWidth(text: string, font: string): number { - const context = _getTextWidthCanvas.getContext('2d')!; - context.font = font; - const metrics = context.measureText(text); - return metrics.width; -} - // ========== For calendar conversion (mostly copied from backend/hist_data/cal.py) ========== export function gregorianToJdn(year: number, month: number, day: number): number { @@ -430,7 +308,7 @@ export function cmpHistEvent(event: HistEvent, event2: HistEvent){ return cmp != 0 ? cmp : event.id - event2.id; } -// ========== For date display ========== +// ========== For [event] date display ========== export function dateToDisplayStr(date: HistDate){ if (date.year <= -1e4){ // N.NNN billion/million/thousand years ago @@ -515,7 +393,6 @@ export function boundedDateToStr(start: HistDate, end: HistDate | null) : string return `${startStr} to ${endStr}`; } - // ========== For server requests ========== const SERVER_DATA_URL = (new URL(window.location.href)).origin + '/data/' @@ -613,7 +490,7 @@ export function jsonToImgInfo(json: ImgInfoJson | null): ImgInfo | null { return json == null ? null : new ImgInfo(json.url, json.license, json.artist, json.credit); } -// ========== For dates in a timeline ========== +// ========== For timeline dates/data ========== export const MIN_DATE = new YearDate(-13.8e9); export const MAX_DATE = new CalDate(2030, 1, 1); @@ -825,8 +702,6 @@ export function dateToScaleDate(date: HistDate, scale: number): HistDate { } } -// ========== For sending timeline-bound data to BaseLine ========== - export class TimelineState { id: number; startDate: HistDate; -- cgit v1.2.3