import {concat, uniq} from "@bokehjs/core/util/array" import {isPlainObject, isArray} from "@bokehjs/core/util/types" export const get = (obj: any, path: string, defaultValue: any = undefined) => { const travel = (regexp: RegExp) => String.prototype.split .call(path, regexp) .filter(Boolean) .reduce((res: any, key: any) => (res !== null && res !== undefined ? res[key] : res), obj) const result = travel(/[,[\]]+?/) || travel(/[,[\].]+?/) return result === undefined || result === obj ? defaultValue : result } export function throttle(func: any, timeFrame: number) { let lastTime: number = 0 return function() { const now: number = Number(new Date()) if (now - lastTime >= timeFrame) { func() lastTime = now } } } export function deepCopy(obj: any): any { let copy // Handle the 3 simple types, and null or undefined if (null == obj || "object" != typeof obj) { return obj } // Handle Array if (obj instanceof Array) { copy = [] for (let i = 0, len = obj.length; i < len; i++) { copy[i] = deepCopy(obj[i]) } return copy } // Handle Object if (obj instanceof Object) { const copy: any = {} for (const attr in obj) { const key: string = attr if (obj.hasOwnProperty(key)) { copy[key] = deepCopy(obj[key]) } } return copy } throw new Error("Unable to copy obj! Its type isn't supported.") } export function reshape(arr: any[], dim: number[]) { let elemIndex = 0 if (!dim || !arr) { return [] } function _nest(dimIndex: number): any[] { let result = [] if (dimIndex === dim.length - 1) { result = concat(arr.slice(elemIndex, elemIndex + dim[dimIndex])) elemIndex += dim[dimIndex] } else { for (let i = 0; i < dim[dimIndex]; i++) { result.push(_nest(dimIndex + 1)) } } return result } return _nest(0) } export async function loadScript(type: string, src: string) { const script = document.createElement("script") script.type = type script.src = src script.defer = true document.head.appendChild(script) return new Promise((resolve, reject) => { script.onload = () => { resolve() } script.onerror = () => { reject() } }) } export function convertUndefined(obj: any): any { if (isArray(obj)) { return obj.map(convertUndefined) } else if (isPlainObject(obj)) { Object .entries(obj) .forEach(([key, value]) => { if (isPlainObject(value) || isArray(value)) { convertUndefined(value) } else if (value === undefined) { obj[key] = null } }) } return obj } export function formatError(error: SyntaxError, code: string): string { const regex = /\((\d+):(\d+)\)/ let msg = `${error}` const match = msg.match(regex) if (!match) { return msg } const line_num = parseInt(match[1]) const col = parseInt(match[2]) const start = Math.max(0, line_num-5) const col_index = line_num-start const lines = code.replace(">", "<").replace("<", ">").split(/\r?\n/).slice(start, line_num+5) msg += "

" for (let i = 0; i < col_index; i++) { const cls = (i == (col_index-1)) ? " class=\"highlight\"" : "" msg += `${lines[i]}` } const indent = " ".repeat(col-1) msg += `
${indent}^
` for (let i = col_index; i < lines.length; i++) { msg += `
${lines[i]}
` } return msg } export function find_attributes(text: string, obj: string, ignored: string[]) { const regex = RegExp(`\\b${obj}\\.([a-zA-Z_][a-zA-Z0-9_]*)\\b`, "g") const matches = [] let match, attr while ((match = regex.exec(text)) !== null && (attr = match[0].slice(obj.length+1)) !== null && !ignored.includes(attr)) { matches.push(attr) } return uniq(matches) } export function schedule_when(func: () => void, predicate: () => boolean, timeout: number = 10): void { const scheduled = () => { if (predicate()) { func() } else { setTimeout(scheduled, timeout) } } scheduled() }