export function mergeTooltips(slider, threshold, separator, fixTooltips) { const isMobile = window.innerWidth <= 650; if (isMobile) { threshold = 25; } const textIsRtl = getComputedStyle(slider).direction === 'rtl'; const isRtl = slider.noUiSlider.options.direction === 'rtl'; const isVertical = slider.noUiSlider.options.orientation === 'vertical'; const tooltips = slider.noUiSlider.getTooltips(); const origins = slider.noUiSlider.getOrigins(); // Move tooltips into the origin element. The default stylesheet handles this. tooltips.forEach(function (tooltip, index) { if (tooltip) { origins[index].appendChild(tooltip); } }); slider.noUiSlider.on('update', function (values, handle, unencoded, tap, positions) { const pools = [[]]; const poolPositions = [[]]; const poolValues = [[]]; let atPool = 0; // Assign the first tooltip to the first pool, if the tooltip is configured if (tooltips[0]) { pools[0][0] = 0; poolPositions[0][0] = positions[0]; poolValues[0][0] = values[0]; } for (let i = 1; i < positions.length; i++) { if (!tooltips[i] || (positions[i] - positions[i - 1]) > threshold) { atPool++; pools[atPool] = []; poolValues[atPool] = []; poolPositions[atPool] = []; } if (tooltips[i]) { pools[atPool].push(i); poolValues[atPool].push(values[i]); poolPositions[atPool].push(positions[i]); } } pools.forEach(function (pool, poolIndex) { const handlesInPool = pool.length; for (let j = 0; j < handlesInPool; j++) { const handleNumber = pool[j]; if (j === handlesInPool - 1) { let offset = 0; poolPositions[poolIndex].forEach(function (value) { offset += 1000 - 10 * value; }); const direction = isVertical ? 'bottom' : 'right'; const last = isRtl ? 0 : handlesInPool - 1; const lastOffset = 1000 - 10 * poolPositions[poolIndex][last]; offset = (textIsRtl && !isVertical ? 100 : 0) + (offset / handlesInPool) - lastOffset; // Center this tooltip over the affected handles tooltips[handleNumber].innerHTML = poolValues[poolIndex].join(separator); tooltips[handleNumber].style.display = 'block'; tooltips[handleNumber].style[direction] = offset + '%'; } else { // Hide this tooltip tooltips[handleNumber].style.display = 'none'; } } }); if (fixTooltips) { const isMobile = window.innerWidth <= 650; const len = isMobile ? 20 : 5; if (positions[0] < len) { tooltips[0].style.right = `${(1 - ((positions[0]) / len)) * -35}px` } else { tooltips[0].style.right = "0" } if (positions[1] > (100 - len)) { tooltips[1].style.right = `${((positions[1] - (100 - len)) / len) * 35}px` } else { tooltips[1].style.right = "0" } } }); } export function burrow(table, addSelfDir, rootName) { const root = {}; table.forEach(row => { let layer = root; row.taxonomy.forEach(key => { layer[key] = key in layer ? layer[key] : {}; layer = layer[key]; }); if (Object.keys(layer).length === 0) { layer["$size$"] = row.size; } else if (addSelfDir) { layer["."] = { "$size$": row.size, }; } }); const descend = function (obj, depth) { return Object.keys(obj).filter(k => k !== "$size$").map(k => { const child = { name: k, depth: depth, value: 0, children: descend(obj[k], depth + 1) }; if ("$size$" in obj[k]) { child.value = obj[k]["$size$"]; } return child; }); }; return { name: rootName, children: descend(root, 1), value: 0, depth: 0, } } export function ext(hit) { return srcExt(hit._source) } export function srcExt(src) { return Object.prototype.hasOwnProperty.call(src, "extension") && src["extension"] !== "" ? "." + src["extension"] : ""; } export function strUnescape(str) { let result = ""; for (let i = 0; i < str.length; i++) { const c = str[i]; const next = str[i + 1]; if (c === "]") { if (next === "]") { result += c; i += 1; } else { result += String.fromCharCode(parseInt(str.slice(i, i + 2), 16)); i += 2; } } else { result += c; } } return result; } const thresh = 1000; const units = ["k", "M", "G", "T", "P", "E", "Z", "Y"]; export function humanFileSize(bytes) { if (bytes === 0) { return "0 B" } if (Math.abs(bytes) < thresh) { return bytes + ' B'; } let u = -1; do { bytes /= thresh; ++u; } while (Math.abs(bytes) >= thresh && u < units.length - 1); return bytes.toFixed(1) + units[u]; } export function humanTime(sec_num) { sec_num = Math.floor(sec_num); const hours = Math.floor(sec_num / 3600); const minutes = Math.floor((sec_num - (hours * 3600)) / 60); const seconds = sec_num - (hours * 3600) - (minutes * 60); if (sec_num < 60) { return `${sec_num}s` } if (sec_num < 3600) { return `${minutes < 10 ? "0" : ""}${minutes}:${seconds < 10 ? "0" : ""}${seconds}`; } return `${hours < 10 ? "0" : ""}${hours}:${minutes < 10 ? "0" : ""}${minutes}:${seconds < 10 ? "0" : ""}${seconds}`; } export function humanDate(numMilis) { const date = (new Date(numMilis * 1000)); return date.getUTCFullYear() + "-" + ("0" + (date.getUTCMonth() + 1)).slice(-2) + "-" + ("0" + date.getUTCDate()).slice(-2) } export function lum(c) { c = c.substring(1); const rgb = parseInt(c, 16); const r = (rgb >> 16) & 0xff; const g = (rgb >> 8) & 0xff; const b = (rgb >> 0) & 0xff; return 0.2126 * r + 0.7152 * g + 0.0722 * b; } export function getSelectedTreeNodes(tree) { const selectedNodes = new Set(); const selected = tree.selected(); for (let i = 0; i < selected.length; i++) { if (selected[i].id === "any") { return ["any"] } //Only get children if (selected[i].text.indexOf("(") !== -1) { if (selected[i].values) { selectedNodes.add(selected[i].values.slice(-1)[0]); } else { selectedNodes.add(selected[i].id); } } } return Array.from(selectedNodes); } export function getTreeNodeAttributes(tree) { const nodes = tree.selectable(); const attributes = {}; for (let i = 0; i < nodes.length; i++) { let id = null; if (nodes[i].text.indexOf("(") !== -1 && nodes[i].values) { id = nodes[i].values.slice(-1)[0]; } else { id = nodes[i].id } attributes[id] = { checked: nodes[i].itree.state.checked, collapsed: nodes[i].itree.state.collapsed, } } return attributes; } export function serializeMimes(mimes) { if (mimes.length === 0) { return undefined; } return mimes.map(mime => compressMime(mime)).join(""); } export function deserializeMimes(mimeString) { return mimeString .replaceAll(/([IVATUF])/g, "$$$&") .split("$") .map(mime => decompressMime(mime)) .slice(1) // Ignore the first (empty) token } export function compressMime(mime) { return mime .replace("image/", "I") .replace("video/", "V") .replace("application/", "A") .replace("text/", "T") .replace("audio/", "U") .replace("font/", "F") .replace("+", ",") .replace("x-", "X") } export function decompressMime(mime) { return mime .replace("I", "image/") .replace("V", "video/") .replace("A", "application/") .replace("T", "text/") .replace("U", "audio/") .replace("F", "font/") .replace(",", "+") .replace("X", "x-") } export function randomSeed() { return Math.round(Math.random() * 100000); } export function sid(doc) { if (doc._id.includes(".")) { return doc._id } const num = BigInt(doc._id); const indexId = (num >> BigInt(32)); const docId = num & BigInt(0xFFFFFFFF); return indexId.toString(16).padStart(8, "0") + "." + docId.toString(16).padStart(8, "0"); }