aboutsummaryrefslogtreecommitdiff
path: root/src/Utils.ts
diff options
context:
space:
mode:
authorbobzel <zzzman@gmail.com>2024-05-16 23:42:12 -0400
committerbobzel <zzzman@gmail.com>2024-05-16 23:42:12 -0400
commit99931c4ca423d51555c24533a98e921c83c941e1 (patch)
tree07baab3cb627eae837c18fb2a6981a746765ec61 /src/Utils.ts
parenta197d8d37e62235bfa59bc8e3bb74c39381c30a4 (diff)
parent69e286b504c2f1bbef7d489dc675b9e54ef8d983 (diff)
Merge branch 'restoringEslint' into eleanor-starter
Diffstat (limited to 'src/Utils.ts')
-rw-r--r--src/Utils.ts892
1 files changed, 121 insertions, 771 deletions
diff --git a/src/Utils.ts b/src/Utils.ts
index 226051f7d..44f1d55c2 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -1,52 +1,17 @@
-import * as Color from 'color';
-import { ColorResult } from 'react-color';
-import * as rp from 'request-promise';
-import { Socket } from 'socket.io';
import * as uuid from 'uuid';
-import { DocumentType } from './client/documents/DocumentTypes';
-import { Colors } from './client/views/global/globalEnums';
-import { DocumentView } from './client/views/nodes/DocumentView';
-import { Message } from './server/Message';
-export namespace Utils {
- export let CLICK_TIME = 300;
- export let DRAG_THRESHOLD = 4;
- export let SNAP_THRESHOLD = 10;
- export function isClick(x: number, y: number, downX: number, downY: number, downTime: number) {
- return Date.now() - downTime < Utils.CLICK_TIME && Math.abs(x - downX) < Utils.DRAG_THRESHOLD && Math.abs(y - downY) < Utils.DRAG_THRESHOLD;
- }
-
- export function cleanDocumentType(type: DocumentType) {
- switch (type) {
- case DocumentType.IMG:
- return 'Image';
- case DocumentType.AUDIO:
- return 'Audio';
- case DocumentType.COL:
- return 'Collection';
- case DocumentType.RTF:
- return 'Text';
- default:
- return type.charAt(0).toUpperCase() + type.slice(1);
- }
- }
-
- export function readUploadedFileAsText(inputFile: File) {
- const temporaryFileReader = new FileReader();
+export function clamp(n: number, lower: number, upper: number) {
+ return Math.max(lower, Math.min(upper, n));
+}
- return new Promise((resolve, reject) => {
- temporaryFileReader.onerror = () => {
- temporaryFileReader.abort();
- reject(new DOMException('Problem parsing input file.'));
- };
+export function ptDistance(p1: { x: number; y: number }, p2: { x: number; y: number }) {
+ return Math.sqrt((p2.x - p1.x) ** 2 + (p2.y - p1.y) ** 2);
+}
- temporaryFileReader.onload = () => {
- resolve(temporaryFileReader.result);
- };
- temporaryFileReader.readAsText(inputFile);
- });
+export namespace Utils {
+ export function GuestID() {
+ return '__guest__';
}
-
export function GenerateGuid(): string {
return uuid.v4();
}
@@ -55,403 +20,138 @@ export namespace Utils {
return uuid.v5(seed, uuid.v5.URL);
}
- export function GuestID() {
- return '__guest__';
- }
+ export const loggingEnabled = false;
+ export const logFilter: number | undefined = undefined;
- /**
- * Uploads an image buffer to the server and stores with specified filename. by default the image
- * is stored at multiple resolutions each retrieved by using the filename appended with _o, _s, _m, _l (indicating original, small, medium, or large)
- * @param imageUri the bytes of the image
- * @param returnedFilename the base filename to store the image on the server
- * @param nosuffix optionally suppress creating multiple resolution images
- */
- export async function convertDataUri(imageUri: string, returnedFilename: string, nosuffix = false, replaceRootFilename?: string) {
- try {
- const posting = Utils.prepend('/uploadURI');
- const returnedUri = await rp.post(posting, {
- body: {
- uri: imageUri,
- name: returnedFilename,
- nosuffix,
- replaceRootFilename,
- },
- json: true,
- });
- return returnedUri;
- } catch (e) {
- console.log('ConvertDataURI :' + e);
+ export function log(prefixIn: string, messageName: string, messageIn: any, receiving: boolean) {
+ let prefix = prefixIn;
+ let message = messageIn;
+ if (!loggingEnabled) {
+ return;
}
- }
-
- export function GetScreenTransform(ele?: HTMLElement | null): { scale: number; translateX: number; translateY: number } {
- if (!ele) {
- return { scale: 1, translateX: 1, translateY: 1 };
+ message = message || {};
+ if (logFilter !== undefined && logFilter !== message.type) {
+ return;
}
- const rect = ele.getBoundingClientRect();
- const scale = ele.offsetWidth === 0 && rect.width === 0 ? 1 : rect.width / ele.offsetWidth;
- const translateX = rect.left;
- const translateY = rect.top;
+ const idString = (message.id || '').padStart(36, ' ');
+ prefix = prefix.padEnd(16, ' ');
+ console.log(`${prefix}: ${idString}, ${receiving ? 'receiving' : 'sending'} ${messageName} with data ${JSON.stringify(message)} `);
+ }
- return { scale, translateX, translateY };
+ export function loggingCallback(prefix: string, func: (args: any) => any, messageName: string) {
+ return (args: any) => {
+ log(prefix, messageName, args, true);
+ func(args);
+ };
}
export function TraceConsoleLog() {
- ['log', 'warn'].forEach(function (method) {
+ ['log', 'warn'].forEach(method => {
const old = (console as any)[method];
- (console as any)[method] = function () {
+ (console as any)[method] = function (...args: any[]) {
let stack = new Error('').stack?.split(/\n/);
// Chrome includes a single "Error" line, FF doesn't.
if (stack && stack[0].indexOf('Error') === 0) {
stack = stack.slice(1);
}
const message = (stack?.[1] || 'Stack undefined!').trim();
- const args = ([] as any[]).slice.apply(arguments).concat([message]);
- return old.apply(console, args);
+ const newArgs = args.slice().concat([message]);
+ return old.apply(console, newArgs);
};
});
}
- /**
- * A convenience method. Prepends the full path (i.e. http://localhost:<port>) to the
- * requested extension
- * @param extension the specified sub-path to append to the window origin
- */
- export function prepend(extension: string): string {
- return window.location.origin + extension;
- }
- export function fileUrl(filename: string): string {
- return prepend(`/files/${filename}`);
- }
-
- export function shareUrl(documentId: string): string {
- return prepend(`/doc/${documentId}?sharing=true`);
- }
-
- export function CorsProxy(url: string): string {
- return prepend('/corsProxy/') + encodeURIComponent(url);
- }
-
- export function CopyText(text: string) {
- navigator.clipboard.writeText(text);
- }
-
- export function decimalToHexString(number: number) {
- if (number < 0) {
- number = 0xffffffff + number + 1;
- }
- return (number < 16 ? '0' : '') + number.toString(16).toUpperCase();
- }
-
- export function colorString(color: ColorResult) {
- return color.hex.startsWith('#') && color.hex.length < 8 ? color.hex + (color.rgb.a ? decimalToHexString(Math.round(color.rgb.a * 255)) : 'ff') : color.hex;
- }
-
- export function fromRGBAstr(rgba: string) {
- const rm = rgba.match(/rgb[a]?\(([ 0-9]+)/);
- const r = rm ? Number(rm[1]) : 0;
- const gm = rgba.match(/rgb[a]?\([ 0-9]+,([ 0-9]+)/);
- const g = gm ? Number(gm[1]) : 0;
- const bm = rgba.match(/rgb[a]?\([ 0-9]+,[ 0-9]+,([ 0-9]+)/);
- const b = bm ? Number(bm[1]) : 0;
- const am = rgba.match(/rgba?\([ 0-9]+,[ 0-9]+,[ 0-9]+,([ .0-9]+)/);
- const a = am ? Number(am[1]) : 1;
- return { r: r, g: g, b: b, a: a };
- }
-
- const isTransparentFunctionHack = 'isTransparent(__value__)';
- export const noRecursionHack = '__noRecursion';
-
- // special case filters
- export const noDragDocsFilter = 'noDragDocs::any::check';
- export const TransparentBackgroundFilter = `backgroundColor::${isTransparentFunctionHack},${noRecursionHack}::check`; // bcz: hack. noRecursion should probably be either another ':' delimited field, or it should be a modifier to the comparision (eg., check, x, etc) field
- export const OpaqueBackgroundFilter = `backgroundColor::${isTransparentFunctionHack},${noRecursionHack}::x`; // bcz: hack. noRecursion should probably be either another ':' delimited field, or it should be a modifier to the comparision (eg., check, x, etc) field
-
- export function IsRecursiveFilter(val: string) {
- return !val.includes(noRecursionHack);
- }
- export function HasFunctionFilter(val: string) {
- if (val.includes(isTransparentFunctionHack)) return (color: string) => color !== '' && DashColor(color).alpha() !== 1;
- // add other function filters here...
- return undefined;
- }
-
- export function toRGBAstr(col: { r: number; g: number; b: number; a?: number }) {
- return 'rgba(' + col.r + ',' + col.g + ',' + col.b + (col.a !== undefined ? ',' + col.a : '') + ')';
- }
-
- export function HSLtoRGB(h: number, s: number, l: number) {
- // Must be fractions of 1
- // s /= 100;
- // l /= 100;
-
- const c = (1 - Math.abs(2 * l - 1)) * s,
- x = c * (1 - Math.abs(((h / 60) % 2) - 1)),
- m = l - c / 2;
- let r = 0,
- g = 0,
- b = 0;
- if (0 <= h && h < 60) {
- r = c;
- g = x;
- b = 0;
- } else if (60 <= h && h < 120) {
- r = x;
- g = c;
- b = 0;
- } else if (120 <= h && h < 180) {
- r = 0;
- g = c;
- b = x;
- } else if (180 <= h && h < 240) {
- r = 0;
- g = x;
- b = c;
- } else if (240 <= h && h < 300) {
- r = x;
- g = 0;
- b = c;
- } else if (300 <= h && h < 360) {
- r = c;
- g = 0;
- b = x;
- }
- r = Math.round((r + m) * 255);
- g = Math.round((g + m) * 255);
- b = Math.round((b + m) * 255);
- return { r: r, g: g, b: b };
- }
-
- export function RGBToHSL(r: number, g: number, b: number) {
- // Make r, g, and b fractions of 1
- r /= 255;
- g /= 255;
- b /= 255;
-
- // Find greatest and smallest channel values
- const cmin = Math.min(r, g, b),
- cmax = Math.max(r, g, b),
- delta = cmax - cmin;
- let h = 0,
- s = 0,
- l = 0;
- // Calculate hue
-
- // No difference
- if (delta === 0) h = 0;
- // Red is max
- else if (cmax === r) h = ((g - b) / delta) % 6;
- // Green is max
- else if (cmax === g) h = (b - r) / delta + 2;
- // Blue is max
- else h = (r - g) / delta + 4;
-
- h = Math.round(h * 60);
-
- // Make negative hues positive behind 360°
- if (h < 0) h += 360; // Calculate lightness
-
- l = (cmax + cmin) / 2;
-
- // Calculate saturation
- s = delta === 0 ? 0 : delta / (1 - Math.abs(2 * l - 1));
-
- // Multiply l and s by 100
- // s = +(s * 100).toFixed(1);
- // l = +(l * 100).toFixed(1);
-
- return { h: h, s: s, l: l };
- }
-
- export function scrollIntoView(targetY: number, targetHgt: number, scrollTop: number, contextHgt: number, minSpacing: number, scrollHeight: number) {
- if (!targetHgt) return targetY; // if there's no height, then assume that
- if (scrollTop + contextHgt < Math.min(scrollHeight, targetY + minSpacing + targetHgt)) {
- return Math.ceil(targetY + minSpacing + targetHgt - contextHgt);
- }
- if (scrollTop >= Math.max(0, targetY - minSpacing)) {
- return Math.max(0, Math.floor(targetY - minSpacing));
- }
- }
-
- export function clamp(n: number, lower: number, upper: number) {
- return Math.max(lower, Math.min(upper, n));
- }
-
- export function distanceBetweenHorizontalLines(xs: number, xe: number, y: number, xs2: number, xe2: number, y2: number): [number, number[]] {
- if ((xs2 <= xs && xe2 >= xs) || (xs2 <= xe && xe2 >= xe) || (xs2 >= xs && xe2 <= xe)) return [Math.abs(y - y2), [Math.max(xs, xs2), y, Math.min(xe, xe2), y]];
- if (xe2 <= xs) return [Math.sqrt((xe2 - xs) * (xe2 - xs) + (y2 - y) * (y2 - y)), [xs, y, xs, y]];
- //if (xs2 > xe)
- return [Math.sqrt((xs2 - xe) * (xs2 - xe) + (y2 - y) * (y2 - y)), [xe, y, xe, y]];
- }
- export function distanceBetweenVerticalLines(x: number, ys: number, ye: number, x2: number, ys2: number, ye2: number): [number, number[]] {
- if ((ys2 <= ys && ye2 >= ys) || (ys2 <= ye && ye2 >= ye) || (ys2 >= ys && ye2 <= ye)) return [Math.abs(x - x2), [x, Math.max(ys, ys2), x, Math.min(ye, ye2)]];
- if (ye2 <= ys) return [Math.sqrt((ye2 - ys) * (ye2 - ys) + (x2 - x) * (x2 - x)), [x, ys, x, ys]];
- //if (ys2 > ye)
- return [Math.sqrt((ys2 - ye) * (ys2 - ye) + (x2 - x) * (x2 - x)), [x, ye, x, ye]];
- }
-
export function rotPt(x: number, y: number, radAng: number) {
return { x: x * Math.cos(radAng) - y * Math.sin(radAng), y: x * Math.sin(radAng) + y * Math.cos(radAng) };
}
- function project(px: number, py: number, ax: number, ay: number, bx: number, by: number) {
- if (ax === bx && ay === by) return { point: { x: ax, y: ay }, left: false, dot: 0, t: 0 };
- const atob = { x: bx - ax, y: by - ay };
- const atop = { x: px - ax, y: py - ay };
- const len = atob.x * atob.x + atob.y * atob.y;
- var dot = atop.x * atob.x + atop.y * atob.y;
- const t = Math.min(1, Math.max(0, dot / len));
-
- dot = (bx - ax) * (py - ay) - (by - ay) * (px - ax);
-
- return {
- point: {
- x: ax + atob.x * t,
- y: ay + atob.y * t,
- },
- left: dot < 1,
- dot: dot,
- t: t,
- };
- }
-
- export function closestPtBetweenRectangles(l: number, t: number, w: number, h: number, l1: number, t1: number, w1: number, h1: number, x: number, y: number) {
- const r = l + w,
- b = t + h;
- const r1 = l1 + w1,
- b1 = t1 + h1;
- const hsegs = [
- [l, r, t, l1, r1, t1],
- [l, r, b, l1, r1, t1],
- [l, r, t, l1, r1, b1],
- [l, r, b, l1, r1, b1],
- ];
- const vsegs = [
- [l, t, b, l1, t1, b1],
- [r, t, b, l1, t1, b1],
- [l, t, b, r1, t1, b1],
- [r, t, b, r1, t1, b1],
- ];
- const res = hsegs.reduce(
- (closest, seg) => {
- const res = distanceBetweenHorizontalLines(seg[0], seg[1], seg[2], seg[3], seg[4], seg[5]);
- return res[0] < closest[0] ? res : closest;
- },
- [Number.MAX_VALUE, []] as [number, number[]]
- );
- const fres = vsegs.reduce((closest, seg) => {
- const res = distanceBetweenVerticalLines(seg[0], seg[1], seg[2], seg[3], seg[4], seg[5]);
- return res[0] < closest[0] ? res : closest;
- }, res);
-
- const near = project(x, y, fres[1][0], fres[1][1], fres[1][2], fres[1][3]);
- return project(near.point.x, near.point.y, fres[1][0], fres[1][1], fres[1][2], fres[1][3]);
- }
-
- export function ptDistance(p1: { x: number; y: number }, p2: { x: number; y: number }) {
- return Math.sqrt((p2.x - p1.x) ** 2 + (p2.y - p1.y) ** 2);
- }
-
- export function getNearestPointInPerimeter(l: number, t: number, w: number, h: number, x: number, y: number) {
- const r = l + w,
- b = t + h;
+ export function getNearestPointInPerimeter(l: number, t: number, w: number, h: number, xIn: number, yIn: number) {
+ const r = l + w;
+ const b = t + h;
- (x = clamp(x, l, r)), (y = clamp(y, t, b));
+ const x = clamp(xIn, l, r);
+ const y = clamp(yIn, t, b);
- const dl = Math.abs(x - l),
- dr = Math.abs(x - r),
- dt = Math.abs(y - t),
- db = Math.abs(y - b);
+ const dl = Math.abs(x - l);
+ const dr = Math.abs(x - r);
+ const dt = Math.abs(y - t);
+ const db = Math.abs(y - b);
const m = Math.min(dl, dr, dt, db);
return m === dt ? [x, t] : m === db ? [x, b] : m === dl ? [l, y] : [r, y];
}
+}
+export function decimalToHexString(numberIn: number) {
+ const number = numberIn < 0 ? 0xffffffff + numberIn + 1 : numberIn;
+ return (number < 16 ? '0' : '') + number.toString(16).toUpperCase();
+}
- export function GetClipboardText(): string {
- const textArea = document.createElement('textarea');
- document.body.appendChild(textArea);
- textArea.focus();
- textArea.select();
-
- try {
- document.execCommand('paste');
- } catch (err) {}
-
- const val = textArea.value;
- document.body.removeChild(textArea);
- return val;
- }
-
- export const loggingEnabled: Boolean = false;
- export const logFilter: number | undefined = undefined;
-
- export function log(prefix: string, messageName: string, message: any, receiving: boolean) {
- if (!loggingEnabled) {
- return;
- }
- message = message || {};
- if (logFilter !== undefined && logFilter !== message.type) {
- return;
- }
- const idString = (message.id || '').padStart(36, ' ');
- prefix = prefix.padEnd(16, ' ');
- console.log(`${prefix}: ${idString}, ${receiving ? 'receiving' : 'sending'} ${messageName} with data ${JSON.stringify(message)} `);
- }
-
- export function loggingCallback(prefix: string, func: (args: any) => any, messageName: string) {
- return (args: any) => {
- log(prefix, messageName, args, true);
- func(args);
- };
- }
-
- export function Emit<T>(socket: Socket, message: Message<T>, args: T) {
- log('Emit', message.Name, args, false);
- socket.emit(message.Message, args);
- }
+export function distanceBetweenHorizontalLines(xs: number, xe: number, y: number, xs2: number, xe2: number, y2: number): [number, number[]] {
+ if ((xs2 <= xs && xe2 >= xs) || (xs2 <= xe && xe2 >= xe) || (xs2 >= xs && xe2 <= xe)) return [Math.abs(y - y2), [Math.max(xs, xs2), y, Math.min(xe, xe2), y]];
+ if (xe2 <= xs) return [Math.sqrt((xe2 - xs) * (xe2 - xs) + (y2 - y) * (y2 - y)), [xs, y, xs, y]];
+ // if (xs2 > xe)
+ return [Math.sqrt((xs2 - xe) * (xs2 - xe) + (y2 - y) * (y2 - y)), [xe, y, xe, y]];
+}
+export function distanceBetweenVerticalLines(x: number, ys: number, ye: number, x2: number, ys2: number, ye2: number): [number, number[]] {
+ if ((ys2 <= ys && ye2 >= ys) || (ys2 <= ye && ye2 >= ye) || (ys2 >= ys && ye2 <= ye)) return [Math.abs(x - x2), [x, Math.max(ys, ys2), x, Math.min(ye, ye2)]];
+ if (ye2 <= ys) return [Math.sqrt((ye2 - ys) * (ye2 - ys) + (x2 - x) * (x2 - x)), [x, ys, x, ys]];
+ // if (ys2 > ye)
+ return [Math.sqrt((ys2 - ye) * (ys2 - ye) + (x2 - x) * (x2 - x)), [x, ye, x, ye]];
+}
- export function AddServerHandler<T>(socket: Socket, message: Message<T>, handler: (args: T) => any) {
- socket.on(message.Message, loggingCallback('Incoming', handler, message.Name));
- }
+function project(px: number, py: number, ax: number, ay: number, bx: number, by: number) {
+ if (ax === bx && ay === by) return { point: { x: ax, y: ay }, left: false, dot: 0, t: 0 };
+ const atob = { x: bx - ax, y: by - ay };
+ const atop = { x: px - ax, y: py - ay };
+ const len = atob.x * atob.x + atob.y * atob.y;
+ let dot = atop.x * atob.x + atop.y * atob.y;
+ const t = Math.min(1, Math.max(0, dot / len));
- export function AddServerHandlerCallback<T>(socket: Socket, message: Message<T>, handler: (args: [T, (res: any) => any]) => any) {
- socket.on(message.Message, (arg: T, fn: (res: any) => any) => {
- log('S receiving', message.Name, arg, true);
- handler([arg, loggingCallback('S sending', fn, message.Name)]);
- });
- }
- export type RoomHandler = (socket: Socket, room: string) => any;
- export type UsedSockets = Socket;
- export type RoomMessage = 'create or join' | 'created' | 'joined';
- export function AddRoomHandler(socket: Socket, message: RoomMessage, handler: RoomHandler) {
- socket.on(message, (room: any) => handler(socket, room));
- }
-}
+ dot = (bx - ax) * (py - ay) - (by - ay) * (px - ax);
-export function OmitKeys(obj: any, keys: string[], pattern?: string, addKeyFunc?: (dup: any) => void): { omit: any; extract: any } {
- const omit: any = { ...obj };
- const extract: any = {};
- keys.forEach(key => {
- extract[key] = omit[key];
- delete omit[key];
- });
- pattern &&
- Array.from(Object.keys(omit))
- .filter(key => key.match(pattern))
- .forEach(key => {
- extract[key] = omit[key];
- delete omit[key];
- });
- addKeyFunc?.(omit);
- return { omit, extract };
+ return {
+ point: {
+ x: ax + atob.x * t,
+ y: ay + atob.y * t,
+ },
+ left: dot < 1,
+ dot: dot,
+ t: t,
+ };
}
-export function WithKeys(obj: any, keys: string[], addKeyFunc?: (dup: any) => void) {
- const dup: any = {};
- keys.forEach(key => (dup[key] = obj[key]));
- addKeyFunc && addKeyFunc(dup);
- return dup;
+export function closestPtBetweenRectangles(l: number, t: number, w: number, h: number, l1: number, t1: number, w1: number, h1: number, x: number, y: number) {
+ const r = l + w;
+ const b = t + h;
+ const r1 = l1 + w1;
+ const b1 = t1 + h1;
+ const hsegs = [
+ [l, r, t, l1, r1, t1],
+ [l, r, b, l1, r1, t1],
+ [l, r, t, l1, r1, b1],
+ [l, r, b, l1, r1, b1],
+ ];
+ const vsegs = [
+ [l, t, b, l1, t1, b1],
+ [r, t, b, l1, t1, b1],
+ [l, t, b, r1, t1, b1],
+ [r, t, b, r1, t1, b1],
+ ];
+ const res = hsegs.reduce(
+ (closest, seg) => {
+ const dist = distanceBetweenHorizontalLines(seg[0], seg[1], seg[2], seg[3], seg[4], seg[5]);
+ return dist[0] < closest[0] ? dist : closest;
+ },
+ [Number.MAX_VALUE, []] as [number, number[]]
+ );
+ const fres = vsegs.reduce((closest, seg) => {
+ const dist = distanceBetweenVerticalLines(seg[0], seg[1], seg[2], seg[3], seg[4], seg[5]);
+ return dist[0] < closest[0] ? dist : closest;
+ }, res);
+
+ const near = project(x, y, fres[1][0], fres[1][1], fres[1][2], fres[1][3]);
+ return project(near.point.x, near.point.y, fres[1][0], fres[1][1], fres[1][2], fres[1][3]);
}
export function timenow() {
@@ -459,7 +159,6 @@ export function timenow() {
let ampm = 'am';
let h = now.getHours();
let m: any = now.getMinutes();
- const s: any = now.getSeconds();
if (h >= 12) {
if (h > 12) h -= 12;
ampm = 'pm';
@@ -468,14 +167,8 @@ export function timenow() {
return now.toLocaleDateString() + ' ' + h + ':' + m + ' ' + ampm;
}
-export function incrementTitleCopy(title: string) {
- const numstr = title.match(/.*(\{([0-9]*)\})+/);
- const copyNumStr = `{${1 + (numstr ? +numstr[2] : 0)}}`;
- return (numstr ? title.replace(numstr[1], '') : title) + copyNumStr;
-}
-
-export function formatTime(time: number) {
- time = Math.round(time);
+export function formatTime(timeIn: number) {
+ const time = Math.round(timeIn);
const hours = Math.floor(time / 60 / 60);
const minutes = Math.floor(time / 60) - hours * 60;
const seconds = time % 60;
@@ -487,11 +180,11 @@ export function aggregateBounds(boundsList: { x: number; y: number; width?: numb
const bounds = boundsList
.map(b => ({ x: b.x, y: b.y, r: b.x + (b.width || 0), b: b.y + (b.height || 0) }))
.reduce(
- (bounds, b) => ({
- x: Math.min(b.x, bounds.x),
- y: Math.min(b.y, bounds.y),
- r: Math.max(b.r, bounds.r),
- b: Math.max(b.b, bounds.b),
+ (prevBounds, b) => ({
+ x: Math.min(b.x, prevBounds.x),
+ y: Math.min(b.y, prevBounds.y),
+ r: Math.max(b.r, prevBounds.r),
+ b: Math.max(b.b, prevBounds.b),
}),
{ x: Number.MAX_VALUE, y: Number.MAX_VALUE, r: -Number.MAX_VALUE, b: -Number.MAX_VALUE }
);
@@ -507,93 +200,31 @@ export function intersectRect(r1: { left: number; top: number; width: number; he
}
export function stringHash(s?: string) {
- return !s ? undefined : Math.abs(s.split('').reduce((a: any, b: any) => (a => a & a)((a << 5) - a + b.charCodeAt(0)), 0));
+ // eslint-disable-next-line no-bitwise
+ return !s ? undefined : Math.abs(s.split('').reduce((a: any, b: any) => (n => n & n)((a << 5) - a + b.charCodeAt(0)), 0));
}
export function percent2frac(percent: string) {
return Number(percent.substr(0, percent.length - 1)) / 100;
}
+export type Without<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
+
+export type Predicate<K, V> = (entry: [K, V]) => boolean;
export function numberRange(num: number) {
return num > 0 && num < 1000 ? Array.from(Array(num)).map((v, i) => i) : [];
}
-export function returnTransparent() {
- return 'transparent';
-}
-
-export function returnTrue() {
- return true;
-}
-
-export function returnIgnore(): 'ignore' {
- return 'ignore';
-}
-export function returnAlways(): 'always' {
- return 'always';
-}
-export function returnNever(): 'never' {
- return 'never';
-}
-
-export function returnDefault(): 'default' {
- return 'default';
-}
-
-export function return18() {
- return 18;
-}
-
-export function returnFalse() {
- return false;
-}
-
-export function returnAll(): 'all' {
- return 'all';
-}
-
-export function returnNone(): 'none' {
- return 'none';
-}
-
-export function returnVal(val1?: number, val2?: number) {
- return val1 ? val1 : val2 !== undefined ? val2 : 0;
-}
-
-export function returnOne() {
- return 1;
-}
-
-export function returnZero() {
- return 0;
-}
-
-export function returnEmptyString() {
- return '';
-}
-
-export function returnEmptyFilter() {
- return [] as string[];
-}
-
-export function returnEmptyDoclist() {
- return [] as any[];
-}
-
-export let emptyPath: DocumentView[] = [];
-
export function emptyFunction() {
return undefined;
}
+export const emptyPath: any[] = [];
+
export function unimplementedFunction() {
throw new Error('This function is not implemented, but should be.');
}
-export type Without<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
-
-export type Predicate<K, V> = (entry: [K, V]) => boolean;
-
export function DeepCopy<K, V>(source: Map<K, V>, predicate?: Predicate<K, V>) {
const deepCopy = new Map<K, V>();
const entries = source.entries();
@@ -620,197 +251,6 @@ export namespace JSONUtils {
}
}
-const easeFunc = (transition: 'ease' | 'linear' | undefined, currentTime: number, start: number, change: number, duration: number) => {
- if (transition === 'linear') {
- let newCurrentTime = currentTime / duration; // currentTime / (duration / 2);
- return start + newCurrentTime * change;
- }
-
- let newCurrentTime = currentTime / (duration / 2);
- if (newCurrentTime < 1) {
- return (change / 2) * newCurrentTime * newCurrentTime + start;
- }
-
- newCurrentTime -= 1;
- return (-change / 2) * (newCurrentTime * (newCurrentTime - 2) - 1) + start;
-};
-
-export function smoothScroll(duration: number, element: HTMLElement | HTMLElement[], to: number, transition: 'ease' | 'linear' | undefined, stopper?: () => void) {
- stopper?.();
- const elements = element instanceof HTMLElement ? [element] : element;
- const starts = elements.map(element => element.scrollTop);
- const startDate = new Date().getTime();
- let _stop = false;
- const stop = () => (_stop = true);
- const animateScroll = () => {
- const currentDate = new Date().getTime();
- const currentTime = currentDate - startDate;
- const setScrollTop = (element: HTMLElement, value: number) => (element.scrollTop = value);
- if (!_stop) {
- if (currentTime < duration) {
- elements.forEach((element, i) => currentTime && setScrollTop(element, easeFunc(transition, Math.min(currentTime, duration), starts[i], to - starts[i], duration)));
- requestAnimationFrame(animateScroll);
- } else {
- elements.forEach(element => setScrollTop(element, to));
- }
- }
- };
- animateScroll();
- return stop;
-}
-
-export function smoothScrollHorizontal(duration: number, element: HTMLElement | HTMLElement[], to: number) {
- const elements = element instanceof HTMLElement ? [element] : element;
- const starts = elements.map(element => element.scrollLeft);
- const startDate = new Date().getTime();
-
- const animateScroll = () => {
- const currentDate = new Date().getTime();
- const currentTime = currentDate - startDate;
- elements.map((element, i) => (element.scrollLeft = easeFunc('ease', currentTime, starts[i], to - starts[i], duration)));
-
- if (currentTime < duration) {
- requestAnimationFrame(animateScroll);
- } else {
- elements.forEach(element => (element.scrollLeft = to));
- }
- };
- animateScroll();
-}
-
-export function addStyleSheet(styleType: string = 'text/css') {
- const style = document.createElement('style');
- style.type = styleType;
- const sheets = document.head.appendChild(style);
- return (sheets as any).sheet;
-}
-export function addStyleSheetRule(sheet: any, selector: any, css: any, selectorPrefix = '.') {
- const propText =
- typeof css === 'string'
- ? css
- : Object.keys(css)
- .map(p => p + ':' + (p === 'content' ? "'" + css[p] + "'" : css[p]))
- .join(';');
- return sheet.insertRule(selectorPrefix + selector + '{' + propText + '}', sheet.cssRules.length);
-}
-export function removeStyleSheetRule(sheet: any, rule: number) {
- if (sheet.rules.length) {
- sheet.removeRule(rule);
- return true;
- }
- return false;
-}
-export function clearStyleSheetRules(sheet: any) {
- if (sheet.rules.length) {
- numberRange(sheet.rules.length).map(n => sheet.removeRule(0));
- return true;
- }
- return false;
-}
-
-export function simulateMouseClick(element: Element | null | undefined, x: number, y: number, sx: number, sy: number, rightClick = true) {
- if (!element) return;
- ['pointerdown', 'pointerup'].map(event => {
- const me = new PointerEvent(event, {
- view: window,
- bubbles: true,
- cancelable: true,
- button: 2,
- pointerType: 'mouse',
- clientX: x,
- clientY: y,
- screenX: sx,
- screenY: sy,
- });
- (me as any).dash = true;
- element.dispatchEvent(me);
- });
-
- if (rightClick) {
- const me = new MouseEvent('contextmenu', {
- view: window,
- bubbles: true,
- cancelable: true,
- button: 2,
- clientX: x,
- clientY: y,
- movementX: 0,
- movementY: 0,
- screenX: sx,
- screenY: sy,
- });
- (me as any).dash = true;
- element.dispatchEvent(me);
- }
-}
-
-export function DashColor(color: string) {
- try {
- return color ? Color(color.toLowerCase()) : Color('transparent');
- } catch (e) {
- if (color.includes('gradient')) console.log("using color 'white' in place of :" + color);
- else console.log('COLOR error:', e);
- return Color('white');
- }
-}
-
-export function lightOrDark(color: any) {
- if (color === 'transparent' || !color) return Colors.BLACK;
- if (color.startsWith?.('linear')) return Colors.BLACK;
- if (DashColor(color).isLight()) return Colors.BLACK;
- return Colors.WHITE;
-}
-
-export function getWordAtPoint(elem: any, x: number, y: number): string | undefined {
- if (elem.tagName === 'INPUT') return 'input';
- if (elem.tagName === 'TEXTAREA') return 'textarea';
- if (elem.nodeType === elem.TEXT_NODE) {
- const range = elem.ownerDocument.createRange();
- range.selectNodeContents(elem);
- var currentPos = 0;
- const endPos = range.endOffset;
- while (currentPos + 1 <= endPos) {
- range.setStart(elem, currentPos);
- range.setEnd(elem, currentPos + 1);
- const rangeRect = range.getBoundingClientRect();
- if (rangeRect.left <= x && rangeRect.right >= x && rangeRect.top <= y && rangeRect.bottom >= y) {
- range.expand?.('word'); // doesn't exist in firefox
- const ret = range.toString();
- range.detach();
- return ret;
- }
- currentPos += 1;
- }
- } else {
- for (const childNode of elem.childNodes) {
- const range = childNode.ownerDocument.createRange();
- range.selectNodeContents(childNode);
- const rangeRect = range.getBoundingClientRect();
- if (rangeRect.left <= x && rangeRect.right >= x && rangeRect.top <= y && rangeRect.bottom >= y) {
- range.detach();
- const word = getWordAtPoint(childNode, x, y);
- if (word) return word;
- } else {
- range.detach();
- }
- }
- }
- return undefined;
-}
-
-export function isTargetChildOf(ele: HTMLDivElement | null, target: Element | null) {
- let entered = false;
- for (let child = target; !entered && child; child = child.parentElement) {
- entered = child === ele;
- }
- return entered;
-}
-
-export function StopEvent(e: React.PointerEvent | React.MouseEvent) {
- e.stopPropagation();
- e.preventDefault();
-}
-
/**
* Helper method for converting pixel string eg. '32px' into number eg. 32
* @param value: string with 'px' ending
@@ -820,100 +260,10 @@ export function StopEvent(e: React.PointerEvent | React.MouseEvent) {
* '32px' -> 32
*/
export function numberValue(value: string | undefined): number {
- if (value == undefined) return 0;
+ if (value === undefined) return 0;
return parseInt(value);
}
export function numbersAlmostEqual(num1: number, num2: number) {
return Math.abs(num1 - num2) < 0.2;
}
-
-export function setupMoveUpEvents(
- target: object,
- e: React.PointerEvent,
- moveEvent: (e: PointerEvent, down: number[], delta: number[]) => boolean,
- upEvent: (e: PointerEvent, movement: number[], isClick: boolean) => any,
- clickEvent: (e: PointerEvent, doubleTap?: boolean) => any,
- stopPropagation: boolean = true,
- stopMovePropagation: boolean = true,
- noDoubleTapTimeout?: () => void
-) {
- const doubleTapTimeout = 300;
- (target as any)._doubleTap = Date.now() - (target as any)._lastTap < doubleTapTimeout;
- (target as any)._lastTap = Date.now();
- (target as any)._downX = (target as any)._lastX = e.clientX;
- (target as any)._downY = (target as any)._lastY = e.clientY;
- (target as any)._noClick = false;
- var moving = false;
-
- const _moveEvent = (e: PointerEvent): void => {
- if (moving || Math.abs(e.clientX - (target as any)._downX) > Utils.DRAG_THRESHOLD || Math.abs(e.clientY - (target as any)._downY) > Utils.DRAG_THRESHOLD) {
- moving = true;
- if ((target as any)._doubleTime) {
- clearTimeout((target as any)._doubleTime);
- (target as any)._doubleTime = undefined;
- }
- if (moveEvent(e, [(target as any)._downX, (target as any)._downY], [e.clientX - (target as any)._lastX, e.clientY - (target as any)._lastY])) {
- document.removeEventListener('pointermove', _moveEvent);
- document.removeEventListener('pointerup', _upEvent);
- }
- }
- (target as any)._lastX = e.clientX;
- (target as any)._lastY = e.clientY;
- stopMovePropagation && e.stopPropagation();
- };
- const _upEvent = (e: PointerEvent): void => {
- const isClick = Math.abs(e.clientX - (target as any)._downX) < 4 && Math.abs(e.clientY - (target as any)._downY) < 4;
- upEvent(e, [e.clientX - (target as any)._downX, e.clientY - (target as any)._downY], isClick);
- if (isClick) {
- if (!(target as any)._doubleTime && noDoubleTapTimeout) {
- (target as any)._doubleTime = setTimeout(() => {
- noDoubleTapTimeout?.();
- (target as any)._doubleTime = undefined;
- }, doubleTapTimeout);
- }
- if ((target as any)._doubleTime && (target as any)._doubleTap) {
- clearTimeout((target as any)._doubleTime);
- (target as any)._doubleTime = undefined;
- }
- (target as any)._noClick = clickEvent(e, (target as any)._doubleTap);
- }
- document.removeEventListener('pointermove', _moveEvent);
- document.removeEventListener('pointerup', _upEvent, true);
- };
- const _clickEvent = (e: MouseEvent): void => {
- if ((target as any)._noClick) e.stopPropagation();
- document.removeEventListener('click', _clickEvent, true);
- };
- if (stopPropagation) {
- e.stopPropagation();
- e.preventDefault();
- }
- document.addEventListener('pointermove', _moveEvent);
- document.addEventListener('pointerup', _upEvent, true);
- document.addEventListener('click', _clickEvent, true);
-}
-
-export function DivHeight(ele: HTMLElement): number {
- return Number(getComputedStyle(ele).height.replace('px', ''));
-}
-export function DivWidth(ele: HTMLElement): number {
- return Number(getComputedStyle(ele).width.replace('px', ''));
-}
-
-export function dateRangeStrToDates(dateStr: string) {
- // dateStr in yyyy-mm-dd format
- const dateRangeParts = dateStr.split('|'); // splits into from and to date
- const fromParts = dateRangeParts[0].split('-');
- const toParts = dateRangeParts[1].split('-');
-
- const fromYear = parseInt(fromParts[0]);
- const fromMonth = parseInt(fromParts[1]) - 1;
- const fromDay = parseInt(fromParts[2]);
-
- const toYear = parseInt(toParts[0]);
- const toMonth = parseInt(toParts[1]) - 1;
- const toDay = parseInt(toParts[2]);
-
- return [new Date(fromYear, fromMonth, fromDay), new Date(toYear, toMonth, toDay)];
-}