aboutsummaryrefslogtreecommitdiff
path: root/src/Utils.ts
diff options
context:
space:
mode:
Diffstat (limited to 'src/Utils.ts')
-rw-r--r--src/Utils.ts251
1 files changed, 213 insertions, 38 deletions
diff --git a/src/Utils.ts b/src/Utils.ts
index 959b89fe5..9a2f01f80 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -3,21 +3,20 @@ import v5 = require("uuid/v5");
import { Socket } from 'socket.io';
import { Message } from './server/Message';
import { RouteStore } from './server/RouteStore';
-import requestPromise = require('request-promise');
-export class Utils {
+export namespace Utils {
- public static DRAG_THRESHOLD = 4;
+ export const DRAG_THRESHOLD = 4;
- public static GenerateGuid(): string {
+ export function GenerateGuid(): string {
return v4();
}
- public static GenerateDeterministicGuid(seed: string): string {
+ export function GenerateDeterministicGuid(seed: string): string {
return v5(seed, v5.URL);
}
- public static GetScreenTransform(ele?: HTMLElement): { scale: number, translateX: number, translateY: number } {
+ export function GetScreenTransform(ele?: HTMLElement): { scale: number, translateX: number, translateY: number } {
if (!ele) {
return { scale: 1, translateX: 1, translateY: 1 };
}
@@ -34,14 +33,23 @@ export class Utils {
* requested extension
* @param extension the specified sub-path to append to the window origin
*/
- public static prepend(extension: string): string {
+ export function prepend(extension: string): string {
return window.location.origin + extension;
}
- public static CorsProxy(url: string): string {
- return this.prepend(RouteStore.corsProxy + "/") + encodeURIComponent(url);
+
+ 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(RouteStore.corsProxy + "/") + encodeURIComponent(url);
}
- public static CopyText(text: string) {
+ export function CopyText(text: string) {
var textArea = document.createElement("textarea");
textArea.value = text;
document.body.appendChild(textArea);
@@ -53,7 +61,95 @@ export class Utils {
document.body.removeChild(textArea);
}
- public static GetClipboardText(): string {
+ export function fromRGBAstr(rgba: string) {
+ let rm = rgba.match(/rgb[a]?\(([0-9]+)/);
+ let r = rm ? Number(rm[1]) : 0;
+ let gm = rgba.match(/rgb[a]?\([0-9]+,([0-9]+)/);
+ let g = gm ? Number(gm[1]) : 0;
+ let bm = rgba.match(/rgb[a]?\([0-9]+,[0-9]+,([0-9]+)/);
+ let b = bm ? Number(bm[1]) : 0;
+ let am = rgba.match(/rgba?\([0-9]+,[0-9]+,[0-9]+,([0-9]+)/);
+ let a = am ? Number(am[1]) : 0;
+ return { r: r, g: g, b: b, a: a };
+ }
+
+ 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;
+
+ let c = (1 - Math.abs(2 * l - 1)) * s,
+ x = c * (1 - Math.abs((h / 60) % 2 - 1)),
+ m = l - c / 2,
+ 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
+ let cmin = Math.min(r, g, b),
+ cmax = Math.max(r, g, b),
+ delta = cmax - cmin,
+ 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 GetClipboardText(): string {
var textArea = document.createElement("textarea");
document.body.appendChild(textArea);
textArea.focus();
@@ -66,51 +162,53 @@ export class Utils {
return val;
}
- public static loggingEnabled: Boolean = false;
- public static logFilter: number | undefined = undefined;
- private static log(prefix: string, messageName: string, message: any, receiving: boolean) {
- if (!this.loggingEnabled) {
+ export const loggingEnabled: Boolean = false;
+ export const logFilter: number | undefined = undefined;
+
+ function log(prefix: string, messageName: string, message: any, receiving: boolean) {
+ if (!loggingEnabled) {
return;
}
message = message || {};
- if (this.logFilter !== undefined && this.logFilter !== message.type) {
+ if (logFilter !== undefined && logFilter !== message.type) {
return;
}
let idString = (message.id || "").padStart(36, ' ');
prefix = prefix.padEnd(16, ' ');
console.log(`${prefix}: ${idString}, ${receiving ? 'receiving' : 'sending'} ${messageName} with data ${JSON.stringify(message)}`);
}
- private static loggingCallback(prefix: string, func: (args: any) => any, messageName: string) {
+
+ function loggingCallback(prefix: string, func: (args: any) => any, messageName: string) {
return (args: any) => {
- this.log(prefix, messageName, args, true);
+ log(prefix, messageName, args, true);
func(args);
};
}
- public static Emit<T>(socket: Socket | SocketIOClient.Socket, message: Message<T>, args: T) {
- this.log("Emit", message.Name, args, false);
+ export function Emit<T>(socket: Socket | SocketIOClient.Socket, message: Message<T>, args: T) {
+ log("Emit", message.Name, args, false);
socket.emit(message.Message, args);
}
- public static EmitCallback<T>(socket: Socket | SocketIOClient.Socket, message: Message<T>, args: T): Promise<any>;
- public static EmitCallback<T>(socket: Socket | SocketIOClient.Socket, message: Message<T>, args: T, fn: (args: any) => any): void;
- public static EmitCallback<T>(socket: Socket | SocketIOClient.Socket, message: Message<T>, args: T, fn?: (args: any) => any): void | Promise<any> {
- this.log("Emit", message.Name, args, false);
+ export function EmitCallback<T>(socket: Socket | SocketIOClient.Socket, message: Message<T>, args: T): Promise<any>;
+ export function EmitCallback<T>(socket: Socket | SocketIOClient.Socket, message: Message<T>, args: T, fn: (args: any) => any): void;
+ export function EmitCallback<T>(socket: Socket | SocketIOClient.Socket, message: Message<T>, args: T, fn?: (args: any) => any): void | Promise<any> {
+ log("Emit", message.Name, args, false);
if (fn) {
- socket.emit(message.Message, args, this.loggingCallback('Receiving', fn, message.Name));
+ socket.emit(message.Message, args, loggingCallback('Receiving', fn, message.Name));
} else {
- return new Promise<any>(res => socket.emit(message.Message, args, this.loggingCallback('Receiving', res, message.Name)));
+ return new Promise<any>(res => socket.emit(message.Message, args, loggingCallback('Receiving', res, message.Name)));
}
}
- public static AddServerHandler<T>(socket: Socket | SocketIOClient.Socket, message: Message<T>, handler: (args: T) => any) {
- socket.on(message.Message, this.loggingCallback('Incoming', handler, message.Name));
+ export function AddServerHandler<T>(socket: Socket | SocketIOClient.Socket, message: Message<T>, handler: (args: T) => any) {
+ socket.on(message.Message, loggingCallback('Incoming', handler, message.Name));
}
- public static AddServerHandlerCallback<T>(socket: Socket, message: Message<T>, handler: (args: [T, (res: any) => any]) => any) {
+ 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) => {
- this.log('S receiving', message.Name, arg, true);
- handler([arg, this.loggingCallback('S sending', fn, message.Name)]);
+ log('S receiving', message.Name, arg, true);
+ handler([arg, loggingCallback('S sending', fn, message.Name)]);
});
}
}
@@ -133,6 +231,39 @@ export function WithKeys(obj: any, keys: string[], addKeyFunc?: (dup: any) => vo
return dup;
}
+export function timenow() {
+ var now = new Date();
+ let ampm = 'am';
+ let h = now.getHours();
+ let m: any = now.getMinutes();
+ let s: any = now.getSeconds();
+ if (h >= 12) {
+ if (h > 12) h -= 12;
+ ampm = 'pm';
+ }
+ if (m < 10) m = '0' + m;
+ return now.toLocaleDateString() + ' ' + h + ':' + m + ' ' + ampm;
+}
+
+export function aggregateBounds(boundsList: { x: number, y: number, width: number, height: number }[]) {
+ return boundsList.reduce((bounds, b) => {
+ var [sptX, sptY] = [b.x, b.y];
+ let [bptX, bptY] = [sptX + b.width, sptY + b.height];
+ return {
+ x: Math.min(sptX, bounds.x), y: Math.min(sptY, bounds.y),
+ r: Math.max(bptX, bounds.r), b: Math.max(bptY, bounds.b)
+ };
+ }, { x: Number.MAX_VALUE, y: Number.MAX_VALUE, r: -Number.MAX_VALUE, b: -Number.MAX_VALUE });
+}
+export function intersectRect(r1: { left: number, top: number, width: number, height: number },
+ r2: { left: number, top: number, width: number, height: number }) {
+ return !(r2.left > r1.left + r1.width || r2.left + r2.width < r1.left || r2.top > r1.top + r1.height || r2.top + r2.height < r1.top);
+}
+
+export function percent2frac(percent: string) {
+ return Number(percent.substr(0, percent.length - 1)) / 100;
+}
+
export function numberRange(num: number) { return Array.from(Array(num)).map((v, i) => i); }
export function returnTrue() { return true; }
@@ -178,12 +309,56 @@ export namespace JSONUtils {
}
-export function PostToServer(relativeRoute: string, body: any) {
- let options = {
- method: "POST",
- uri: Utils.prepend(relativeRoute),
- json: true,
- body: body
+const easeInOutQuad = (currentTime: number, start: number, change: number, duration: number) => {
+ 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, to: number) {
+ const start = element.scrollTop;
+ const change = to - start;
+ const startDate = new Date().getTime();
+
+ const animateScroll = () => {
+ const currentDate = new Date().getTime();
+ const currentTime = currentDate - startDate;
+ element.scrollTop = easeInOutQuad(currentTime, start, change, duration);
+
+ if (currentTime < duration) {
+ requestAnimationFrame(animateScroll);
+ } else {
+ element.scrollTop = to;
+ }
};
- return requestPromise.post(options);
+ animateScroll();
+}
+export function addStyleSheet(styleType: string = "text/css") {
+ let style = document.createElement("style");
+ style.type = styleType;
+ var sheets = document.head.appendChild(style);
+ return (sheets as any).sheet;
+}
+export function addStyleSheetRule(sheet: any, selector: any, css: any) {
+ var propText = typeof css === "string" ? css : Object.keys(css).map(p => p + ":" + (p === "content" ? "'" + css[p] + "'" : css[p])).join(";");
+ return sheet.insertRule("." + 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;
} \ No newline at end of file