UNPKG

dropflow

Version:

A small CSS2 document renderer built from specifications

223 lines (222 loc) 5.68 kB
import { G_ID, G_CL, G_SZ } from './text-harfbuzz.js'; /** * Binary search that returns the position `x` should be in */ export function binarySearch(a, x) { let l = 0, r = a.length - 1; while (true) { let i = Math.floor((l + r) / 2); if (a[i] < x) { l = i + 1; if (l > r) return l; } else if (a[i] > x) { r = i - 1; if (r < l) return i; } else { return i; } } } /** * Binary search that returns the position `x` should be in, using the `end` * property of objects in the `a` array */ export function binarySearchOf(a, x, end) { let l = 0, r = a.length - 1; if (r < 0) return -1; while (true) { let i = Math.floor((l + r) / 2); if (end(a[i]) < x) { l = i + 1; if (l > r) return l; } else if (end(a[i]) > x) { r = i - 1; if (r < l) return i; } else { return i; } } } /** * Binary search that returns the position `x` should be in, using the second * value in a tuple in the `a` array */ export function binarySearchTuple(a, x) { let l = 0, r = a.length - 1; if (r < 0) return -1; while (true) { let i = Math.floor((l + r) / 2); if (a[i][1] < x) { l = i + 1; if (l > r) return l; } else if (a[i][1] > x) { r = i - 1; if (r < l) return i; } else { return i; } } } let _id = 0; export function id() { return String(_id++); } const bytes = new Uint8Array(16); export function uuid() { let uuid = ''; crypto.getRandomValues(bytes); // Set version (4) in the most significant 4 bits of byte 6 bytes[6] = (bytes[6] & 0x0f) | 0x40; // Set variant (10) in the most significant 2 bits of byte 8 bytes[8] = (bytes[8] & 0x3f) | 0x80; for (let i = 0; i < bytes.length; i++) { switch (i) { case 4: case 6: case 8: case 10: uuid += '-'; } uuid += bytes[i].toString(16).padStart(2, '0'); } return uuid; } export function loggableText(text) { return text.replace(/\n/g, '⏎').replace(/\t/g, '␉'); } export function basename(url) { return url.href.slice(url.href.lastIndexOf('/') + 1); } export class Logger { string; formats; // only for browsers indent; lineIsEmpty; constructor() { this.string = ''; this.formats = []; this.indent = []; this.lineIsEmpty = false; } bold() { if (typeof process === 'object') { this.string += '\x1b[1m'; } else { this.string += '%c'; this.formats.push('font-weight: bold'); } } underline() { if (typeof process === 'object') { this.string += '\x1b[4m'; } else { this.string += '%c'; this.formats.push('text-decoration: underline'); } } dim() { if (typeof process === 'object') { this.string += '\x1b[2m'; } else { this.string += '%c'; this.formats.push('color: gray'); } } reset() { if (typeof process === 'object') { this.string += '\x1b[0m'; } else { this.string += '%c'; this.formats.push('font-weight: normal'); } } flush() { console.log(this.string, ...this.formats); this.string = ''; this.formats = []; } text(str) { const lines = String(str).split('\n'); const append = (s) => { if (s) { if (this.lineIsEmpty) this.string += this.indent.join(''); this.string += s; this.lineIsEmpty = false; } }; for (let i = 0; i < lines.length; i++) { if (i === 0) { append(lines[i]); } else { this.string += '\n'; this.lineIsEmpty = true; append(lines[i]); } } } glyphs(glyphs) { for (let i = 0; i < glyphs.length; i += G_SZ) { const cl = glyphs[i + G_CL]; const isp = i - G_SZ >= 0 && glyphs[i - G_SZ + G_CL] === cl; const isn = i + G_SZ < glyphs.length && glyphs[i + G_SZ + G_CL] === cl; if (isp || isn) this.bold(); if (isn && !isp) this.text('('); this.text(glyphs[i + G_ID]); if (!isn && isp) this.text(')'); this.text(' '); if (isp || isn) this.reset(); } } pushIndent(indent = ' ') { this.indent.push(indent); } popIndent() { this.indent.pop(); } } export class Deferred { status; promise; resolve; reject; constructor() { this.status = 'unresolved'; this.promise = new Promise((resolve, reject) => { this.resolve = (t) => { if (this.status === 'unresolved') { this.status = 'resolved'; resolve(t); } }; this.reject = (e) => { if (this.status === 'unresolved') { this.status = 'rejected'; reject(e); } }; }); } }