All files / src/web url-state.js

100% Statements 25/25
100% Branches 13/13
100% Functions 6/6
100% Lines 20/20

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59            100x                 378x 378x 378x 378x 378x         173x         26x                 192x 192x 254x 71x 71x   183x 183x     192x 176x 157x       162x 162x      
import { buildUrlFromParams, parseLocationParams } from "../url-params.js";
import { debounce } from "../debounce.js";
import { urlParamType } from "./parameters.js";
import { noteLocation } from "./analytics.js";
 
// A slider fires for every pixel of a drag, and each URL update is a history entry.
const UrlSettleMs = 300;
 
/**
 * The page's settings as carried in its URL. `params` is the one parsed
 * object: whoever changes a setting hands the change to set(), which edits
 * it in place and writes the URL in one step.
 */
export class UrlState {
    constructor(location, history, typeOf = urlParamType) {
        this.history = history;
        this.typeOf = typeOf;
        this.baseUrl = location.origin + location.pathname;
        this.params = parseLocationParams(location, typeOf);
        this.updateUrlOnceSettled = debounce(() => this.updateUrl(), UrlSettleMs);
    }
 
    /** The page's URL with the parameters as they are now. */
    url() {
        return buildUrlFromParams(this.baseUrl, this.params, this.typeOf);
    }
 
    /** The page's URL with some parameters changed, leaving `params` as it is. */
    urlWith(overrides) {
        return buildUrlFromParams(this.baseUrl, { ...this.params, ...overrides }, this.typeOf);
    }
 
    /**
     * Apply `changes` to the parameters (undefined deletes a key) and push the
     * new URL; `settle: true` lets a burst of changes finish before pushing one
     * history entry for the lot.
     */
    set(changes, { settle = false } = {}) {
        let changed = false;
        for (const [key, value] of Object.entries(changes)) {
            if (value === undefined) {
                if (key in this.params) changed = true;
                delete this.params[key];
            } else {
                if (this.params[key] !== value) changed = true;
                this.params[key] = value;
            }
        }
        if (!changed) return;
        if (settle) this.updateUrlOnceSettled();
        else this.updateUrl();
    }
 
    updateUrl() {
        this.history.pushState(null, null, this.url());
        noteLocation();
    }
}