Xmrit

by Commoncog

Integrating with Xmrit

The easiest way to integrate with Xmrit is via Xmrit’s share link. A Xmrit’s share link contains all the needed data to render a XmR chart in the link itself, making it easily shareable, embeddable and portable.

Sharelink Specification

The specification of the share links are as follows

  • v: This is the version parameter. It should be the string “0”.
  • d: This is the data parameter. It is made out of two parts separated by a dot ("."). The part before the dot is a lz77 compressed string of a CSV (comma-separated values) string. The first two values of the CSV are the two axis labels (xLabel and yLabel). If these axis labels contain comma (,), we will replace it with semicolon (;). The rest of the CSV values are the date columns. If the date contains comma (,), we will replace it with semicolon (;). The part after the dot is a base64-no-padding-url-encoded float32 array of the value columns. base64-no-padding-url-encoded means that you remove all equal signs from the end of the string, replace plus (+) with dash (-) and replace forward slash (-) with underscore (/).
  • l: This is the locked limit parameters. It is a base64-no-padding-url-encoded float32 array of the following values: avgX, avgMovement, LNPL, UNPL, URL, lockedLimitStatus. All values except lockedLimitStatus accept floating point numbers. Values for lockedLimitStatus are 0 for unlocked and 1 for locked.
  • s: This is the dividers (separators) parameters. It is a base64-no-padding-url-encoded float32 array of dividers’ x values. X values are dates represented as Unix epoch timestamp in milliseconds.

An example of encoding a share link in Javascript is given in the generateShareLink function below

import lz77 from "lz77";

/**
 * Sharelink v0:
 * - v: 0
 * - d: lz77.compress(xLabel,yLabel,date-cols...).base64(float32array of value-cols).
 * - l: float32array of [avgX, avgMovement, LNPL, UNPL, URL, lockedLimitStatus]
 * - s: float32array of dividers x values (unix timestamp in milliseconds)
 * @returns 
 */
function generateShareLink(s: {
    xLabel: string,
    yLabel: string,
    xdata: {
        x: string, // date in yyyy-mm-dd
        value: number
    }[],
    dividerLines?: {
        id: string,
        x: number // date in unix milliseconds
    }[],
    lockedLimits?: {
        avgX: number,
        avgMovement: number,
        UNPL: number, // upper natural process limit
        LNPL: number, // lower natural process limit
        URL: number, // upper range limit
    },
    lockedLimitStatus?: number,
}): string {
    // Search parameters can also be an object
    const paramsObj = {
        v: "0" // version
    };
    let validXdata = s.xdata.filter(dv => dv.x || dv.value)
    // basically first 2 are labels, followed by date-column, followed by value-column.
    // date-column are compressed using lz77
    // value-column are encoded as bytearray and converted into base64 string
    const dateText = `${s.xLabel.replace(",", ";")},${s.yLabel.replace(",", ";")},` +
        validXdata.map((d) => {
            if (d.x) {
                return d.x.replace(",", ";")
            } else {
                return ''
            }
        }).join(",")
    const valueText = validXdata.map((d) => d.value)
    paramsObj['d'] = btoaUrlSafe(lz77.compress(dateText)) + '.' +
        encodeNumberArrayString(valueText)
    if (s.dividerLines) {
        const dividers = encodeNumberArrayString(s.dividerLines.map(dl => dl.x))
        if (dividers.length > 0) {
            paramsObj['s'] = dividers
        }
    }
    if (s.lockedLimits && (s.lockedLimitStatus & 1) == 1) {
        paramsObj['l'] = encodeNumberArrayString([
            s.lockedLimits.avgX,
            s.lockedLimits.avgMovement,
            s.lockedLimits.LNPL,
            s.lockedLimits.UNPL,
            s.lockedLimits.URL,
            s.lockedLimitStatus
        ])
    }
    const searchParams = new URLSearchParams(paramsObj);
    const fullPath = `https://xmrit.com?${searchParams.toString()}`
    return fullPath
}

function encodeNumberArrayString(input: number[]) {
    const buffer = new ArrayBuffer(input.length * 4);
    const view = new DataView(buffer);
    input.forEach((i, idx) => {
        view.setFloat32(idx * 4, i)
    })
    return btoaUrlSafe(
        new Uint8Array(buffer)
            .reduce((data, byte) => data + String.fromCharCode(byte), '')
    )
}

function btoaUrlSafe(s: string) {
    return btoa(s).replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '')
}
Last Updated: 21 Mar 2024