3.3 KiB · text History e43831b
.pragma library

// Shared vocabulary for every view. Mirrors the Android client's Models.kt
// so the widget and the phone agree on names, ladders and addresses.

// Accepts what a person would actually type: a bare host, host:port, or a
// full url. No https coercion — LambentEx is plain http on a LAN, and port
// 8033 is assumed, the same as BaseUrlStore.normalize.
function normalizeBase(input) {
    var trimmed = (input || "").trim().replace(/\/+$/, "")
    if (trimmed === "") return ""
    var withScheme = /^https?:\/\//.test(trimmed) ? trimmed : "http://" + trimmed
    var hostPart = withScheme.replace(/^https?:\/\//, "").split("/")[0]
    return hostPart.indexOf(":") !== -1 ? withScheme : withScheme + ":8033"
}

function socketUrl(base) {
    var b = normalizeBase(base)
    if (b === "") return ""
    return b.replace(/^http:\/\//, "ws://").replace(/^https:\/\//, "wss://") + "/socket/websocket?vsn=2.0.0"
}

// the speed ladder, named the way the web UI names it
function speedLabel(ms) {
    switch (ms) {
    case 10: return "HUNDREDTHS"
    case 50: return "FHUNDREDTHS"
    case 100: return "TENTHS"
    case 500: return "FTENTHS"
    case 1000: return "ONES"
    case 2000: return "TWOS"
    case 5000: return "FIVES"
    case 10000: return "TENS"
    case 20000: return "TWENTYS"
    case 30000: return "THIRTYS"
    case 60000: return "MINS"
    default: return ms + "ms"
    }
}

function percent(v) {
    return Math.floor((v || 0) / 255 * 100)
}

var FULL = 255

// Where a value sits on the brightness ladder, interpolated between rungs.
// The rungs are not evenly spaced in 0..255, so a slider that lays them out
// evenly needs this to put a walking value in the right place.
function ladderPosition(value, ladder) {
    if (!ladder || ladder.length === 0) return 0
    var last = ladder.length - 1
    if (value <= ladder[0].value) return 0
    if (value >= ladder[last].value) return last
    for (var i = 1; i <= last; i++) {
        if (ladder[i].value > value) {
            var low = ladder[i - 1].value
            var high = ladder[i].value
            return (i - 1) + (value - low) / (high - low)
        }
    }
    return last
}

// the rung a value is nearest, by name, or a percentage when off-ladder
function rungName(value, ladder) {
    if (!ladder || ladder.length === 0) return percent(value) + "%"
    var idx = Math.round(ladderPosition(value, ladder))
    var rung = ladder[idx]
    return rung && rung.value === value ? rung.name : percent(value) + "%"
}

function fadeName(rate, fades) {
    for (var i = 0; i < (fades || []).length; i++) {
        if (fades[i].rate === rate) return fades[i].name
    }
    return rate + "ms"
}

// Down to at most `n` pixels, keeping their spread, so a circle split into
// wedges stays readable at panel size.
function resample(pixels, n) {
    if (pixels.length <= n) return pixels
    var out = []
    for (var i = 0; i < n; i++) out.push(pixels[Math.floor(i * pixels.length / n)])
    return out
}

function cssRgb(px, scale) {
    var s = scale === undefined ? 1 : scale
    function ch(v) { return Math.max(0, Math.min(255, Math.round((v || 0) * s))) }
    return "rgb(" + ch(px[0]) + "," + ch(px[1]) + "," + ch(px[2]) + ")"
}

function deviceLabel(device, mac) {
    return (device && device.name) || mac
}

function chanLabel(chan) {
    return chan === "all" || chan === undefined || chan === null ? "all" : "ch " + chan
}