cables_dev/cables/src/core/utils.js
-
- /**
- * @namespace external:CABLES#Utils
- */
-
- import { CONSTANTS } from "./constants.js";
-
- const UTILS = {};
- /**
- * Merge two Float32Arrays.
- * @function float32Concat
- * @memberof Utils
- * @param {Float32Array} first Left-hand side array
- * @param {Float32Array} second Right-hand side array
- * @return {Float32Array}
- * @static
- */
- UTILS.float32Concat = function (first, second)
- {
- if (!(first instanceof Float32Array)) first = new Float32Array(first);
- if (!(second instanceof Float32Array)) second = new Float32Array(second);
-
- const result = new Float32Array(first.length + second.length);
-
- result.set(first);
- result.set(second, first.length);
-
- return result;
- };
-
- /**
- * get op shortname: only last part of fullname and without version
- * @function getShortOpName
- * @memberof CABLES
- * @param {string} fullname full op name
- * @static
- */
- export const getShortOpName = function (fullname)
- {
- let name = fullname.split(".")[fullname.split(".").length - 1];
-
- if (name.contains(CONSTANTS.OP.OP_VERSION_PREFIX))
- {
- const n = name.split(CONSTANTS.OP.OP_VERSION_PREFIX)[1];
- name = name.substring(0, name.length - (CONSTANTS.OP.OP_VERSION_PREFIX + n).length);
- }
- return name;
- };
-
- /**
- * randomize order of an array
- * @function shuffleArray
- * @memberof Utils
- * @param {Array|Float32Array} array {Array} original
- * @return {Array|Float32Array} shuffled array
- * @static
- */
- export const shuffleArray = function (array)
- {
- for (let i = array.length - 1; i > 0; i--)
- {
- const j = Math.floor(Math.seededRandom() * (i + 1));
- const temp = array[i];
- array[i] = array[j];
- array[j] = temp;
- }
- return array;
- };
-
-
- /**
- * generate a short "relativly unique" id
- * @function shortId
- * @memberof Utils
- * @return {String} generated ID
- * @static
- */
-
- const _shortIds = {};
- const _shortId = function ()
- {
- let str = Math.random().toString(36).substr(2, 9);
-
- if (_shortIds.hasOwnProperty(str)) str = _shortId();
- _shortIds[str] = true;
- return str;
- };
- export const shortId = _shortId;
-
-
- /**
- * generate a UUID
- * @function uuid
- * @memberof Utils
- * @return {String} generated UUID
- * @static
- */
- const _uuid = function ()
- {
- let d = new Date().getTime();
- const uuid = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) =>
- {
- const r = (d + Math.random() * 16) % 16 | 0;
- d = Math.floor(d / 16);
- return (c == "x" ? r : (r & 0x3) | 0x8).toString(16);
- });
- return uuid;
- };
- export const uuid = _uuid;
- export const generateUUID = _uuid;
-
-
-
- export function cleanJson(obj)
- {
- for (const i in obj)
- {
- if (obj[i] && typeof objValue === "object" && obj[i].constructor === Object) obj[i] = cleanJson(obj[i]);
-
- if (obj[i] === null || obj[i] === undefined) delete obj[i];
- else if (Array.isArray(obj[i]) && obj[i].length == 0) delete obj[i];
- }
-
- return obj;
- }
-
-
- /**
- * @see http://stackoverflow.com/q/7616461/940217
- * @memberof Utils
- * @param str
- * @param prefix
- * @return {string}
- */
- const _prefixedHash = function (str, prefix = "id")
- {
- let hash = 0;
- if (Array.prototype.reduce)
- {
- hash = str.split("").reduce((a, b) => { a = ((a << 5) - a) + b.charCodeAt(0); return a & a; }, 0);
- }
- else
- {
- if (str.length > 0)
- {
- for (let i = 0; i < str.length; i++)
- {
- let character = str.charCodeAt(i);
- hash = ((hash << 5) - hash) + character;
- hash &= hash; // Convert to 32bit integer
- }
- }
- }
- return prefix + "" + hash;
- };
- export const prefixedHash = _prefixedHash;
-
- /**
- * generate a simple ID
- * @function simpleId
- * @memberof Utils
- * @return {Number} new id
- * @static
- */
- let simpleIdCounter = 0;
- export const simpleId = function ()
- {
- simpleIdCounter++;
- return simpleIdCounter;
- };
-
- /**
- * smoothStep a value
- * @function smoothStep
- * @memberof Utils
- * @function
- * @param {Number} perc value value to be smoothed [0-1]
- * @return {Number} smoothed value
- * @static
- */
- export const smoothStep = function (perc)
- {
- const x = Math.max(0, Math.min(1, (perc - 0) / (1 - 0)));
- perc = x * x * (3 - 2 * x); // smoothstep
- return perc;
- };
-
- /**
- * smootherstep a value
- * @function smootherStep
- * @memberof Utils
- * @param {Number} perc value to be smoothed [0-1]
- * @return {Number} smoothed value
- * @static
- */
- export const smootherStep = function (perc)
- {
- const x = Math.max(0, Math.min(1, (perc - 0) / (1 - 0)));
- perc = x * x * x * (x * (x * 6 - 15) + 10); // smootherstep
- return perc;
- };
-
-
- /**
- * clamp number / make sure its between min/max
- * @function clamp
- * @memberof Utils
- * @param {Number} value value to be mapped
- * @param {Number} min minimum value
- * @param {Number} max maximum value
- * @static
- */
- export const clamp = function (value, min, max)
- {
- return Math.min(Math.max(value, min), max);
- };
-
- /**
- * map a value in a range to a value in another range
- * @function map
- * @memberof Utils
- * @param {Number} x value to be mapped
- * @param {Number} _oldMin old range minimum value
- * @param {Number} _oldMax old range maximum value
- * @param {Number} _newMin new range minimum value
- * @param {Number} _newMax new range maximum value
- * @param {Number} _easing
- * @return {Number} mapped value
- * @static
- */
- export const map = function (x, _oldMin, _oldMax, _newMin, _newMax, _easing)
- {
- if (x >= _oldMax) return _newMax;
- if (x <= _oldMin) return _newMin;
-
- let reverseInput = false;
- const oldMin = Math.min(_oldMin, _oldMax);
- const oldMax = Math.max(_oldMin, _oldMax);
- if (oldMin != _oldMin) reverseInput = true;
-
- let reverseOutput = false;
- const newMin = Math.min(_newMin, _newMax);
- const newMax = Math.max(_newMin, _newMax);
- if (newMin != _newMin) reverseOutput = true;
-
- let portion = 0;
- let r = 0;
-
- if (reverseInput) portion = ((oldMax - x) * (newMax - newMin)) / (oldMax - oldMin);
- else portion = ((x - oldMin) * (newMax - newMin)) / (oldMax - oldMin);
-
- if (reverseOutput) r = newMax - portion;
- else r = portion + newMin;
-
- if (!_easing) return r;
- if (_easing == 1)
- {
- // smoothstep
- x = Math.max(0, Math.min(1, (r - _newMin) / (_newMax - _newMin)));
- return _newMin + x * x * (3 - 2 * x) * (_newMax - _newMin);
- }
- if (_easing == 2)
- {
- // smootherstep
- x = Math.max(0, Math.min(1, (r - _newMin) / (_newMax - _newMin)));
- return _newMin + x * x * x * (x * (x * 6 - 15) + 10) * (_newMax - _newMin);
- }
-
- return r;
- };
-
- /**
- * @namespace Math
- */
- /**
- * set random seed for seededRandom()
- * @memberof Math
- * @type Number
- * @static
- */
- Math.randomSeed = 1;
-
-
- Math.setRandomSeed = function (seed)
- {
- // https://github.com/cables-gl/cables_docs/issues/622
- Math.randomSeed = seed * 50728129;
- if (seed != 0)
- {
- Math.randomSeed = Math.seededRandom() * 17624813;
- Math.randomSeed = Math.seededRandom() * 9737333;
- }
- };
-
-
- /**
- * generate a seeded random number
- * @function seededRandom
- * @memberof Math
- * @param {Number} max minimum possible random number
- * @param {Number} min maximum possible random number
- * @return {Number} random value
- * @static
- */
- Math.seededRandom = function (max, min)
- {
- if (Math.randomSeed === 0) Math.randomSeed = Math.random() * 999;
- max = max || 1;
- min = min || 0;
-
- Math.randomSeed = (Math.randomSeed * 9301 + 49297) % 233280;
- const rnd = Math.randomSeed / 233280.0;
-
- return min + rnd * (max - min);
- };
-
-
- // ----------------------------------------------------------------
-
- /**
- * returns true if parameter is a number
- * @function isNumeric
- * @memberof Utils
- * @param {Any} n value The value to check.
- * @return {Boolean}
- * @static
- */
- UTILS.isNumeric = function (n)
- {
- return !isNaN(parseFloat(n)) && isFinite(n);
- };
-
- /**
- * returns true if parameter is array
- * @function isArray
- * @param {Any} v value Value to check
- * @memberof Utils
- * @return {Boolean}
- * @static
- */
- UTILS.isArray = function (v)
- {
- return Object.prototype.toString.call(v) === "[object Array]";
- };
-
- /**
- * @namespace String
- */
-
- /**
- * append a linebreak to a string
- * @function endl
- * @memberof String
- * @return {String} string with newline break appended ('\n')
- */
- String.prototype.endl = function ()
- {
- return this + "\n";
- };
-
- /**
- * return true if string starts with prefix
- * @function startsWith
- * @memberof String
- * @param {String} prefix The prefix to check.
- * @return {Boolean}
- */
- String.prototype.startsWith = function (prefix)
- {
- if (!this || !prefix) return false;
- if (this.length >= prefix.length)
- {
- if (this.substring(0, prefix.length) == prefix) return true;
- }
- return false;
- // return this.indexOf(prefix) === 0;
- };
-
- /**
- * return true if string ends with suffix
- * @function endsWith
- * @memberof String
- * @param {String} suffix
- * @return {Boolean}
- */
- String.prototype.endsWith = String.prototype.endsWith || function (suffix)
- {
- return this.match(suffix + "$") == suffix;
- };
-
- /**
- * return true if string contains string
- * @function contains
- * @memberof String
- * @param {String} searchStr
- * @return {Boolean}
- */
- String.prototype.contains = String.prototype.contains || function (searchStr)
- {
- return this.indexOf(searchStr) > -1;
- };
-
-
-
- // ----------------------------------------------------------------
-
- /**
- * append a unique/random parameter to a url, so the browser is forced to reload the file, even if its cached
- * @function cacheBust
- * @static
- * @memberof Utils
- * @param {String} url The url to append the cachebuster parameter to.
- * @return {String} url with cachebuster parameter
- */
- export const cacheBust = function (url = "")
- {
- if (!url) return "";
- if (url.startsWith("data:")) return;
- if (url.contains("?")) url += "&";
- else url += "?";
- return url + "cache=" + CABLES.uuid();
- };
-
- /**
- * copy the content of an array
- * @function copyArray
- * @static
- * @memberof Utils
- * @param {Array} src sourceArray
- * @param {Array} dst optional
- * @return {Array} dst
- */
- export const copyArray = function (src, dst)
- {
- if (!src) return null;
- dst = dst || [];
- dst.length = src.length;
- for (let i = 0; i < src.length; i++)
- {
- dst[i] = src[i];
- }
-
- return dst;
- };
-
-
- /**
- * return the filename part of a url without extension
- * @function basename
- * @static
- * @memberof Utils
- * @param {String} url
- * @return {String} just the filename
- */
- export const basename = function (url)
- {
- let name = CABLES.filename(url);
-
- const parts2 = name.split(".");
- name = parts2[0];
-
- return name;
- };
-
- /**
- * output a stacktrace to the console
- * @function logStack
- * @static
- * @memberof Utils
- */
- export const logStack = function ()
- {
- console.log("logstack", (new Error()).stack);
- };
-
- /**
- * return the filename part of a url
- * @function filename
- * @static
- * @memberof Utils
- * @param {String} url
- * @return {String} just the filename
- */
- export const filename = function (url)
- {
- let name = "";
- if (!url) return "";
-
- if (url.startsWith("data:") && url.contains(":"))
- {
- const parts = url.split(",");
- return parts[0];
- }
-
- let parts = (url + "").split("/");
- if (parts.length > 0)
- {
- const str = parts[parts.length - 1];
- let parts2 = str.split("?");
- name = parts2[0];
- }
-
- return name || "";
- };
-
-
- export const ajaxSync = function (url, cb, method, post, contenttype)
- {
- request({
- "url": url,
- "cb": cb,
- "method": method,
- "data": post,
- "contenttype": contenttype,
- "sync": true,
- });
- };
-
- /**
- * make an ajax request
- * @static
- * @function ajax
- * @param url
- * @param cb
- * @param method
- * @param post
- * @param contenttype
- * @param jsonP
- * @param headers
- * @param options
- */
- export const ajax = function (url, cb, method, post, contenttype, jsonP, headers = {}, options = {})
- {
- const requestOptions = {
- "url": url,
- "cb": cb,
- "method": method,
- "data": post,
- "contenttype": contenttype,
- "sync": false,
- "jsonP": jsonP,
- "headers": headers,
- };
- if (options && options.credentials) requestOptions.credentials = options.credentials;
- request(requestOptions);
- };
-
- export const request = function (options)
- {
- if (!options.hasOwnProperty("asynch")) options.asynch = true;
-
- let xhr;
- try
- {
- xhr = new XMLHttpRequest();
- }
- catch (e) {}
-
- xhr.onreadystatechange = function ()
- {
- if (xhr.readyState != 4) return;
-
- if (options.cb)
- {
- if (xhr.status == 200 || xhr.status == 0) options.cb(false, xhr.responseText, xhr);
- else options.cb(true, xhr.responseText, xhr);
- }
- };
-
- try
- {
- xhr.open(options.method ? options.method.toUpperCase() : "GET", options.url, !options.sync);
- }
- catch (e)
- {
- if (options.cb && e) options.cb(true, e.msg, xhr);
- }
-
- if (typeof options.headers === "object")
- {
- if (options.headers)
- {
- const keys = Object.keys(options.headers);
- for (let i = 0; i < keys.length; i++)
- {
- const name = keys[i];
- const value = options.headers[name];
- xhr.setRequestHeader(name, value);
- }
- }
- }
-
- if (options.credentials && options.credentials !== "omit")
- {
- xhr.withCredentials = true;
- }
-
- try
- {
- if (!options.post && !options.data)
- {
- xhr.send();
- }
- else
- {
- xhr.setRequestHeader(
- "Content-type",
- options.contenttype ? options.contenttype : "application/x-www-form-urlencoded",
- );
- xhr.send(options.data || options.post);
- }
- }
- catch (e)
- {
- if (options.cb) options.cb(true, e.msg, xhr);
- }
- };
-
-
- export const keyCodeToName = function (keyCode)
- {
- if (!keyCode && keyCode !== 0) return "Unidentified";
- const keys = {
- "8": "Backspace",
- "9": "Tab",
- "12": "Clear",
- "13": "Enter",
- "16": "Shift",
- "17": "Control",
- "18": "Alt",
- "19": "Pause",
- "20": "CapsLock",
- "27": "Escape",
- "32": "Space",
- "33": "PageUp",
- "34": "PageDown",
- "35": "End",
- "36": "Home",
- "37": "ArrowLeft",
- "38": "ArrowUp",
- "39": "ArrowRight",
- "40": "ArrowDown",
- "45": "Insert",
- "46": "Delete",
- "112": "F1",
- "113": "F2",
- "114": "F3",
- "115": "F4",
- "116": "F5",
- "117": "F6",
- "118": "F7",
- "119": "F8",
- "120": "F9",
- "121": "F10",
- "122": "F11",
- "123": "F12",
- "144": "NumLock",
- "145": "ScrollLock",
- "224": "Meta"
- };
- if (keys[keyCode])
- {
- return keys[keyCode];
- }
- else
- {
- return String.fromCharCode(keyCode);
- }
- };
- // ----------------------------------------------------------------
-
- window.performance = window.performance || {
- "offset": Date.now(),
- "now": function now()
- {
- return Date.now() - this.offset;
- },
- };
-
-
- export const logErrorConsole = function (initiator)
- {
- CABLES.errorConsole = CABLES.errorConsole || { "log": [] };
- CABLES.errorConsole.log.push({ "initiator": initiator, "arguments": arguments });
-
- if (!CABLES.errorConsole.ele)
- {
- const ele = document.createElement("div");
- ele.id = "cablesErrorConsole";
- ele.style.width = "90%";
- ele.style.height = "300px";
- ele.style.zIndex = "9999999";
- ele.style.display = "inline-block";
- ele.style.position = "absolute";
- ele.style.padding = "10px";
- ele.style.fontFamily = "monospace";
- ele.style.color = "red";
- ele.style.backgroundColor = "#200";
-
- CABLES.errorConsole.ele = ele;
- document.body.appendChild(ele);
- }
-
- let logHtml = "ERROR<br/>for more info, open your browsers dev tools console (Ctrl+Shift+I or Command+Alt+I)<br/>";
-
- for (let l = 0; l < CABLES.errorConsole.log.length; l++)
- {
- logHtml += CABLES.errorConsole.log[l].initiator + " ";
- for (let i = 1; i < CABLES.errorConsole.log[l].arguments.length; i++)
- {
- if (i > 2)logHtml += ", ";
- let arg = CABLES.errorConsole.log[l].arguments[i];
- if (arg.constructor.name.indexOf("Error") > -1 || arg.constructor.name.indexOf("error") > -1)
- {
- let txt = "Uncaught ErrorEvent ";
- if (arg.message)txt += " message: " + arg.message;
- logHtml += txt;
- }
- else if (typeof arg == "string")
- logHtml += arg;
- else if (typeof arg == "number")
- logHtml += String(arg) + " ";
- }
- logHtml += "<br/>";
- }
-
-
- CABLES.errorConsole.ele.innerHTML = logHtml;
- };
-
-
- export { UTILS };