# Source: helpers/transforms.js

    /**
     * Transformation functions: used to transform a raw data value. For example, a template or axis label
     *  can convert from pvalue to -log10pvalue by specifying the following field name (the `|funcname` syntax
     *  indicates applying a function):
     *
     * `{{assoc:pvalue|neglog10}}`
     *
     * Transforms can also be chained so that several are used in order from left to right:
     * `{{log_pvalue|logtoscinotation|htmlescape}}`
     *
     * Most parts of LocusZoom that rely on being given a field name (or value) can be used this way: axis labels, position,
     *  match/filter logic, tooltip HTML template, etc. If your use case is not working with filters, please file a
     *  bug report!
     *
     * NOTE: for best results, don't specify filters in the `fields` array of a data layer- only specify them where the
     *  transformed value will be used.
     * @module LocusZoom_TransformationFunctions
     */

    /**
     * Return the log10 of a value. Can be applied several times in a row for, eg, loglog plots.
     * @param {number} value
     * @return {null|number}
     */
    export function log10 (value) {
        if (isNaN(value) || value <= 0) {
            return null;
        }
        return Math.log(value) / Math.LN10;
    }

    /**
     * Return the -log (base 10), a common means of representing pvalues in locuszoom plots
     * @param {number} value
     * @return {number}
     */
    export function neglog10 (value) {
        if (isNaN(value) || value <= 0) {
            return null;
        }
        return -Math.log(value) / Math.LN10;
    }

    /**
     * Convert a number from logarithm to scientific notation. Useful for, eg, a datasource that returns -log(p) by default
     * @param {number} value
     * @return {string}
     */
    export function logtoscinotation (value) {
        if (isNaN(value)) {
            return 'NaN';
        }
        if (value === 0) {
            return '1';
        }
        const exp = Math.ceil(value);
        const diff = exp - value;
        const base = Math.pow(10, diff);
        if (exp === 1) {
            return (base / 10).toFixed(4);
        } else if (exp === 2) {
            return (base / 100).toFixed(3);
        } else {
            return `${base.toFixed(2)} × 10^-${exp}`;
        }
    }

    /**
     * Represent a number in scientific notation
     * @param {Number} value
     * @returns {String}
     */
    export function scinotation (value) {
        if (isNaN(value)) {
            return 'NaN';
        }
        if (value === 0) {
            return '0';
        }

        const abs = Math.abs(value);
        let log;
        if (abs > 1) {
            log = Math.ceil(Math.log(abs) / Math.LN10);
        } else {  // 0...1
            log = Math.floor(Math.log(abs) / Math.LN10);
        }
        if (Math.abs(log) <= 3) {
            return value.toFixed(3);
        } else {
            return value.toExponential(2).replace('+', '').replace('e', ' × 10^');
        }
    }

    /**
     * HTML-escape user entered values for use in constructed HTML fragments
     *
     * For example, this filter can be used on tooltips with custom HTML display. This protects against some forms of
     *  XSS injection when plotting user-provided data, as well as display artifacts from field values with HTML symbols
     *  such as `<` or `>`.
     * @param {String} value HTML-escape the provided value
     * @return {string}
     */
    export function htmlescape (value) {
        if (!value) {
            return '';
        }
        value = `${value}`;

        return value.replace(/['"<>&`]/g, function (s) {
            switch (s) {
            case "'":
                return '&#039;';
            case '"':
                return '&quot;';
            case '<':
                return '&lt;';
            case '>':
                return '&gt;';
            case '&':
                return '&amp;';
            case '`':
                return '&#x60;';
            }
        });
    }

    /**
     * Return true if the value is numeric (including 0)
     *
     * This is useful in template code, where we might wish to hide a field that is absent, but show numeric values even if they are 0
     *   Eg, `{{#if value|is_numeric}}...{{/if}}
     *
     * @param {Number} value
     * @return {boolean}
     */
    export function is_numeric(value) {
        return typeof value === 'number';
    }

    /**
     * URL-encode the provided text, eg for constructing hyperlinks
     * @param {String} value
     * @return {string}
     */
    export function urlencode (value) {
        return encodeURIComponent(value);
    }