"use strict";
/**
 * Copyright (c) 2023
 *
 * Common utility functions for ThoughtSpot Visual Embed SDK
 * @summary Utils
 * @author Ayon Ghosh <ayon.ghosh@thoughtspot.com>
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.resetValueFromWindow = exports.getValueFromWindow = exports.storeValueInWindow = exports.getTypeFromValue = exports.isUndefined = exports.removeStyleProperties = exports.setStyleProperties = exports.removeTypename = exports.getOperationNameFromQuery = exports.deepMerge = exports.getDOMNode = exports.getRuntimeFilters = exports.getCustomisations = exports.checkReleaseVersionInBeta = exports.setAttributes = exports.embedEventStatus = exports.getOffsetTop = exports.getEncodedQueryParamsString = exports.getRedirectUrl = exports.appendToUrlHash = exports.getSSOMarker = exports.getCssDimension = exports.getQueryParamString = exports.getRuntimeParameters = exports.getFilterQuery = void 0;
const tslib_1 = require("tslib");
const ts_deepmerge_1 = tslib_1.__importDefault(require("ts-deepmerge"));
/**
 * Construct a runtime filters query string from the given filters.
 * Refer to the following docs for more details on runtime filter syntax:
 * https://cloud-docs.thoughtspot.com/admin/ts-cloud/apply-runtime-filter.html
 * https://cloud-docs.thoughtspot.com/admin/ts-cloud/runtime-filter-operators.html
 * @param runtimeFilters
 */
const getFilterQuery = (runtimeFilters) => {
    if (runtimeFilters && runtimeFilters.length) {
        const filters = runtimeFilters.map((filter, valueIndex) => {
            const index = valueIndex + 1;
            const filterExpr = [];
            filterExpr.push(`col${index}=${encodeURIComponent(filter.columnName)}`);
            filterExpr.push(`op${index}=${filter.operator}`);
            filterExpr.push(filter.values.map((value) => {
                const encodedValue = typeof value === 'bigint' ? value.toString() : value;
                return `val${index}=${encodeURIComponent(String(encodedValue))}`;
            }).join('&'));
            return filterExpr.join('&');
        });
        return `${filters.join('&')}`;
    }
    return null;
};
exports.getFilterQuery = getFilterQuery;
/**
 * Construct a runtime parameter override query string from the given option.
 * @param runtimeParameters
 */
const getRuntimeParameters = (runtimeParameters) => {
    if (runtimeParameters && runtimeParameters.length) {
        const params = runtimeParameters.map((param, valueIndex) => {
            const index = valueIndex + 1;
            const filterExpr = [];
            filterExpr.push(`param${index}=${encodeURIComponent(param.name)}`);
            filterExpr.push(`paramVal${index}=${encodeURIComponent(param.value)}`);
            return filterExpr.join('&');
        });
        return `${params.join('&')}`;
    }
    return null;
};
exports.getRuntimeParameters = getRuntimeParameters;
/**
 * Convert a value to a string representation to be sent as a query
 * parameter to the ThoughtSpot app.
 * @param value Any parameter value
 */
const serializeParam = (value) => {
    // do not serialize primitive types
    if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
        return value;
    }
    return JSON.stringify(value);
};
/**
 * Convert a value to a string:
 * in case of an array, we convert it to CSV.
 * in case of any other type, we directly return the value.
 * @param value
 */
const paramToString = (value) => (Array.isArray(value) ? value.join(',') : value);
/**
 * Return a query param string composed from the given params object
 * @param queryParams
 * @param shouldSerializeParamValues
 */
const getQueryParamString = (queryParams, shouldSerializeParamValues = false) => {
    const qp = [];
    const params = Object.keys(queryParams);
    params.forEach((key) => {
        const val = queryParams[key];
        if (val !== undefined) {
            const serializedValue = shouldSerializeParamValues
                ? serializeParam(val)
                : paramToString(val);
            qp.push(`${key}=${serializedValue}`);
        }
    });
    if (qp.length) {
        return qp.join('&');
    }
    return null;
};
exports.getQueryParamString = getQueryParamString;
/**
 * Get a string representation of a dimension value in CSS
 * If numeric, it is considered in pixels.
 * @param value
 */
const getCssDimension = (value) => {
    if (typeof value === 'number') {
        return `${value}px`;
    }
    return value;
};
exports.getCssDimension = getCssDimension;
const getSSOMarker = (markerId) => {
    const encStringToAppend = encodeURIComponent(markerId);
    return `tsSSOMarker=${encStringToAppend}`;
};
exports.getSSOMarker = getSSOMarker;
/**
 * Append a string to a URL's hash fragment
 * @param url A URL
 * @param stringToAppend The string to append to the URL hash
 */
const appendToUrlHash = (url, stringToAppend) => {
    let outputUrl = url;
    const encStringToAppend = encodeURIComponent(stringToAppend);
    const marker = `tsSSOMarker=${encStringToAppend}`;
    let splitAdder = '';
    if (url.indexOf('#') >= 0) {
        // If second half of hash contains a '?' already add a '&' instead of
        // '?' which appends to query params.
        splitAdder = url.split('#')[1].indexOf('?') >= 0 ? '&' : '?';
    }
    else {
        splitAdder = '#?';
    }
    outputUrl = `${outputUrl}${splitAdder}${marker}`;
    return outputUrl;
};
exports.appendToUrlHash = appendToUrlHash;
/**
 *
 * @param url
 * @param stringToAppend
 * @param path
 */
function getRedirectUrl(url, stringToAppend, path = '') {
    const targetUrl = path ? new URL(path, window.location.origin).href : url;
    return (0, exports.appendToUrlHash)(targetUrl, stringToAppend);
}
exports.getRedirectUrl = getRedirectUrl;
const getEncodedQueryParamsString = (queryString) => {
    if (!queryString) {
        return queryString;
    }
    return btoa(queryString).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
};
exports.getEncodedQueryParamsString = getEncodedQueryParamsString;
const getOffsetTop = (element) => {
    const rect = element.getBoundingClientRect();
    return rect.top + window.scrollY;
};
exports.getOffsetTop = getOffsetTop;
exports.embedEventStatus = {
    START: 'start',
    END: 'end',
};
const setAttributes = (element, attributes) => {
    Object.keys(attributes).forEach((key) => {
        element.setAttribute(key, attributes[key].toString());
    });
};
exports.setAttributes = setAttributes;
const isCloudRelease = (version) => version.endsWith('.cl');
/* For Search Embed: ReleaseVersionInBeta */
const checkReleaseVersionInBeta = (releaseVersion, suppressBetaWarning) => {
    if (releaseVersion !== '' && !isCloudRelease(releaseVersion)) {
        const splittedReleaseVersion = releaseVersion.split('.');
        const majorVersion = Number(splittedReleaseVersion[0]);
        const isBetaVersion = majorVersion < 8;
        return !suppressBetaWarning && isBetaVersion;
    }
    return false;
};
exports.checkReleaseVersionInBeta = checkReleaseVersionInBeta;
const getCustomisations = (embedConfig, viewConfig) => {
    var _a, _b, _c, _d;
    const customizationsFromViewConfig = viewConfig.customizations;
    const customizationsFromEmbedConfig = embedConfig.customizations
        || embedConfig.customisations;
    const customizations = {
        style: {
            ...customizationsFromEmbedConfig === null || customizationsFromEmbedConfig === void 0 ? void 0 : customizationsFromEmbedConfig.style,
            ...customizationsFromViewConfig === null || customizationsFromViewConfig === void 0 ? void 0 : customizationsFromViewConfig.style,
            customCSS: {
                ...(_a = customizationsFromEmbedConfig === null || customizationsFromEmbedConfig === void 0 ? void 0 : customizationsFromEmbedConfig.style) === null || _a === void 0 ? void 0 : _a.customCSS,
                ...(_b = customizationsFromViewConfig === null || customizationsFromViewConfig === void 0 ? void 0 : customizationsFromViewConfig.style) === null || _b === void 0 ? void 0 : _b.customCSS,
            },
            customCSSUrl: ((_c = customizationsFromViewConfig === null || customizationsFromViewConfig === void 0 ? void 0 : customizationsFromViewConfig.style) === null || _c === void 0 ? void 0 : _c.customCSSUrl)
                || ((_d = customizationsFromEmbedConfig === null || customizationsFromEmbedConfig === void 0 ? void 0 : customizationsFromEmbedConfig.style) === null || _d === void 0 ? void 0 : _d.customCSSUrl),
        },
        content: {
            ...customizationsFromEmbedConfig === null || customizationsFromEmbedConfig === void 0 ? void 0 : customizationsFromEmbedConfig.content,
            ...customizationsFromViewConfig === null || customizationsFromViewConfig === void 0 ? void 0 : customizationsFromViewConfig.content,
        },
    };
    return customizations;
};
exports.getCustomisations = getCustomisations;
const getRuntimeFilters = (runtimefilters) => (0, exports.getFilterQuery)(runtimefilters || []);
exports.getRuntimeFilters = getRuntimeFilters;
/**
 * Gets a reference to the DOM node given
 * a selector.
 * @param domSelector
 */
function getDOMNode(domSelector) {
    return typeof domSelector === 'string' ? document.querySelector(domSelector) : domSelector;
}
exports.getDOMNode = getDOMNode;
const deepMerge = (target, source) => (0, ts_deepmerge_1.default)(target, source);
exports.deepMerge = deepMerge;
const getOperationNameFromQuery = (query) => {
    const regex = /(?:query|mutation)\s+(\w+)/;
    const matches = query.match(regex);
    return matches === null || matches === void 0 ? void 0 : matches[1];
};
exports.getOperationNameFromQuery = getOperationNameFromQuery;
/**
 *
 * @param obj
 */
function removeTypename(obj) {
    if (!obj || typeof obj !== 'object')
        return obj;
    // eslint-disable-next-line no-restricted-syntax
    for (const key in obj) {
        if (key === '__typename') {
            delete obj[key];
        }
        else if (typeof obj[key] === 'object') {
            removeTypename(obj[key]);
        }
    }
    return obj;
}
exports.removeTypename = removeTypename;
/**
 * Sets the specified style properties on an HTML element.
 * @param {HTMLElement} element - The HTML element to which the styles should be applied.
 * @param {Partial<CSSStyleDeclaration>} styleProperties - An object containing style
 * property names and their values.
 * @example
 * // Apply styles to an element
 * const element = document.getElementById('myElement');
 * const styles = {
 *   backgroundColor: 'red',
 *   fontSize: '16px',
 * };
 * setStyleProperties(element, styles);
 */
const setStyleProperties = (element, styleProperties) => {
    if (!(element === null || element === void 0 ? void 0 : element.style))
        return;
    Object.keys(styleProperties).forEach((styleProperty) => {
        element.style[styleProperty] = styleProperties[styleProperty].toString();
    });
};
exports.setStyleProperties = setStyleProperties;
/**
 * Removes specified style properties from an HTML element.
 * @param {HTMLElement} element - The HTML element from which the styles should be removed.
 * @param {string[]} styleProperties - An array of style property names to be removed.
 * @example
 * // Remove styles from an element
 * const element = document.getElementById('myElement');
 * element.style.backgroundColor = 'red';
 * const propertiesToRemove = ['backgroundColor'];
 * removeStyleProperties(element, propertiesToRemove);
 */
const removeStyleProperties = (element, styleProperties) => {
    if (!(element === null || element === void 0 ? void 0 : element.style))
        return;
    styleProperties.forEach((styleProperty) => {
        element.style.removeProperty(styleProperty);
    });
};
exports.removeStyleProperties = removeStyleProperties;
const isUndefined = (value) => value === undefined;
exports.isUndefined = isUndefined;
// Return if the value is a string, double or boolean.
const getTypeFromValue = (value) => {
    if (typeof value === 'string') {
        return ['char', 'string'];
    }
    if (typeof value === 'number') {
        return ['double', 'double'];
    }
    if (typeof value === 'boolean') {
        return ['boolean', 'boolean'];
    }
    return ['', ''];
};
exports.getTypeFromValue = getTypeFromValue;
const sdkWindowKey = '_tsEmbedSDK';
/**
 * Stores a value in the global `window` object under the `_tsEmbedSDK` namespace.
 * @param key - The key under which the value will be stored.
 * @param value - The value to store.
 * @param options - Additional options.
 * @param options.ignoreIfAlreadyExists - Does not set if value for key is set.
 *
 * @returns The stored value.
 *
 * @version SDK: 1.36.2 | ThoughtSpot: *
 */
function storeValueInWindow(key, value, options = {}) {
    if (!window[sdkWindowKey]) {
        window[sdkWindowKey] = {};
    }
    if (options.ignoreIfAlreadyExists && key in window[sdkWindowKey]) {
        return window[sdkWindowKey][key];
    }
    window[sdkWindowKey][key] = value;
    return value;
}
exports.storeValueInWindow = storeValueInWindow;
/**
 * Retrieves a stored value from the global `window` object under the `_tsEmbedSDK` namespace.
 * @param key - The key whose value needs to be retrieved.
 * @returns The stored value or `undefined` if the key is not found.
 */
const getValueFromWindow = (key) => { var _a; return (_a = window === null || window === void 0 ? void 0 : window[sdkWindowKey]) === null || _a === void 0 ? void 0 : _a[key]; };
exports.getValueFromWindow = getValueFromWindow;
/**
 * Resets the key if it exists in the `window` object under the `_tsEmbedSDK` key.
 * Returns true if the key was reset, false otherwise.
 * @param key - Key to reset
 * @returns - boolean indicating if the key was reset
 */
function resetValueFromWindow(key) {
    if (key in window[sdkWindowKey]) {
        delete window[sdkWindowKey][key];
        return true;
    }
    return false;
}
exports.resetValueFromWindow = resetValueFromWindow;
//# sourceMappingURL=utils.js.map