text: import React from "react";
  
  function matchAll(string, regex) {
    const matches = [];
    string.replace(regex, function () {
      const match = Array.prototype.slice.call(arguments, 0, -2);
      match.input = arguments[arguments.length - 1];
      match.index = arguments[arguments.length - 2];
      matches.push(match);
    });
    return matches;
  }
  
  function formatScientificNotation(
    value,
    sciMagUpThresh = 1,
    sciMagDownThresh = 1,
    sciDecimals = 1,
    fixedDeminals = 1,
    minFixedDecimals = 0,
    html = true
  ) {
    if (value == null || isNaN(value) || value === undefined) return null;
  
    const absValue = Math.abs(value);
    const sign = Math.sign(value);
    const exp = Math.floor(Math.log10(absValue));
  
    if (
      absValue !== 0 &&
      (absValue > Math.pow(10, sciMagUpThresh) ||
        absValue < Math.pow(10, -sciMagDownThresh))
    ) {
      const sciVal = ((sign * absValue) / Math.pow(10, exp)).toFixed(sciDecimals);
      if (html) {
        return (
          <span className="sci-notation">
            {sciVal}&thinsp;&times;&thinsp;10<sup>{exp}</sup>
          </span>
        );
      } else {
        return sciVal + " × 10^" + exp;
      }
    } else if (absValue > 1 || absValue === 0) {
      return value.toFixed(fixedDeminals);
    } else {
      const decimals = Math.max(minFixedDecimals, fixedDeminals - exp);
      return value.toFixed(decimals);
    }
  }
  
  function formatChemicalFormula(formula) {
    if (!formula) {
      return null;
    }
  
    const regex = /([A-Z][a-z]?)([0-9]*)/g;
    const formattedFormula = [];
    for (const match of matchAll(formula, regex)) {
      if (parseFloat(match[2]) === 0) {
        continue;
      }
  
      if (parseFloat(match[2]) === 1) {
        match[2] = "";
      }
  
      formattedFormula.push(
        <span key={match[1]}>
          {match[1]}
          {match[2] && <sub>{match[2]}</sub>}
        </span>
      );
    }
    return formattedFormula;
  }
  
  function dictOfArraysToArrayOfDicts(dictOfArrays) {
    const arrayOfDicts = [];
    for (const key in dictOfArrays) {
      if (Array.isArray(dictOfArrays[key])) {
        for (let iEl = 0; iEl < dictOfArrays[key].length; iEl++) {
          if (iEl > arrayOfDicts.length - 1) {
            arrayOfDicts.push({});
          }
          arrayOfDicts[iEl][key] = dictOfArrays[key][iEl];
        }
      } else {
        const iEl = 0;
        if (iEl > arrayOfDicts.length - 1) {
          arrayOfDicts.push({});
        }
        arrayOfDicts[iEl][key] = dictOfArrays[key];
      }
    }
  
    return arrayOfDicts;
  }
  
  function upperCaseFirstLetter(string) {
    return (
      string.substring(0, 1).toUpperCase() + string.substring(1, string.length)
    );
  }
  
  function scrollTo(el, offsetTop = -52) {
    window.scrollTo({ behavior: "smooth", top: el.offsetTop + offsetTop });
  }
  
  function strCompare(a, b, caseInsensitive = true) {
    if (caseInsensitive) {
      a = a.toLowerCase();
      b = b.toLowerCase();
    }
  
    if (a < b) {
      return -1;
    }
  
    if (a > b) {
      return 1;
    }
  
    return 0;
  }
  
  function removeDuplicates(array, keyFunc = null) {
    const uniqueKeyVals = {};
  
    for (const el of array) {
      let key;
      if (keyFunc == null) {
        key = el;
      } else {
        key = keyFunc(el);
      }
  
      if (!(key in uniqueKeyVals)) {
        uniqueKeyVals[key] = el;
      }
    }
  
    return Object.values(uniqueKeyVals);
  }
  
  function downloadData(data, filename, mimeType) {
    const anchor = document.createElement("a");
  
    anchor.download = filename;
  
    const blob = new Blob([data], { type: mimeType });
    const url = URL.createObjectURL(blob);
    anchor.href = url;
  
    const clickHandler = () => {
      setTimeout(() => {
        URL.revokeObjectURL(url);
        anchor.removeEventListener("click", clickHandler);
      }, 150);
    };
    anchor.addEventListener("click", clickHandler, false);
  
    anchor.click();
  }
  
  function parseHistoryLocationPathname(history) {
    const pathRegex = /^(\/(.*?)(\/(.*?)(\/(.*?))?)?)?\/?$/;
    const match = history.location.pathname.match(pathRegex);
    let route = null;
    let query = null;
    let organism = null;
    if (match) {
      route = match[2];
      query = match[4] ? decodeURIComponent(match[4].trim()) || null : null;
      organism = match[6] ? decodeURIComponent(match[6].trim()) || null : null;
    }
    if (typeof organism === "string" && organism.toLowerCase() === "null") {
      organism = null;
    }
    return {
      route: route,
      query: query,
      organism: organism,
    };
  }
  
  function getNumProperties(obj) {
    let size = 0;
    for (const key in obj) {
      if (Object.prototype.hasOwnProperty.call(obj, key)) {
        size++;
      }
    }
    return size;
  }
  
  function castToArray(obj) {
    if (Array.isArray(obj)) {
      return obj;
    } else {
      if (obj == null || obj === undefined) {
        return [];
      } else {
        return [obj];
      }
    }
  }
  
  function numberWithCommas(x) {
    return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
  }
  
  function isEmpty(element) {
    if (element instanceof Array) {
      if (element.length === 0) {
        return true;
      }
    } else {
      if (Object.keys(element).length === 0) {
        return true;
      }
    }
    return false;
  }
  
  function naturalSort(a, b) {
    return a.localeCompare(b, navigator.languages[0] || navigator.language, {
      caseFirst: "lower",
      numeric: true,
      ignorePunctuation: false,
    });
  }
  
  function isOrthoDbId(id) {
    return id.match(/^\d/i) != null;
  }
  
  function isUniProtId(id) {
    return (
      id.match(
        /^([A-N,R-Z][0-9]([A-Z][A-Z, 0-9][A-Z, 0-9][0-9]){1,2})|([O,P,Q][0-9][A-Z, 0-9][A-Z, 0-9][A-Z, 0-9][0-9])(\.\d+)?$/
      ) != null
    );
  }
  
  function replaceNanWithNull(value) {
    if (Number.isNaN(value)) {
      return null;
    }
  
    if (
      value != null &&
      value !== undefined &&
      (Array.isArray(value) || value.constructor === Object)
    ) {
      const toReplace = [value];
      while (toReplace.length) {
        const val = toReplace.pop();
  
        if (Array.isArray(val)) {
          for (let i = 0; i < val.length; i++) {
            const v = val[i];
            if (Number.isNaN(v)) {
              val[i] = null;
            } else if (
              v != null &&
              v !== undefined &&
              (Array.isArray(v) || v.constructor === Object)
            ) {
              toReplace.push(v);
            }
          }
        } else if (Object.keys(val).length) {
          for (const key in val) {
            const v = val[key];
            if (Number.isNaN(v)) {
              val[key] = null;
            } else if (
              v != null &&
              v !== undefined &&
              (Array.isArray(v) || v.constructor === Object)
            ) {
              toReplace.push(v);
            }
          }
        }
      }
    }
  
    return value;
  }
  
  const httpRequestLog = [];
  
  const isiOS =
    [
      "iPad Simulator",
      "iPhone Simulator",
      "iPod Simulator",
      "iPad",
      "iPhone",
      "iPod",
    ].includes(navigator.platform) ||
    // iPad on iOS 13 detection
    (navigator.userAgent.includes("Mac") && "ontouchend" in document);
  
  export {
    formatScientificNotation,
    formatChemicalFormula,
    dictOfArraysToArrayOfDicts,
    upperCaseFirstLetter,
    scrollTo,
    strCompare,
    removeDuplicates,
    downloadData,
    parseHistoryLocationPathname,
    getNumProperties,
    castToArray,
    numberWithCommas,
    isEmpty,
    naturalSort,
    isOrthoDbId,
    isUniProtId,
    replaceNanWithNull,
    httpRequestLog,
    isiOS,
  };
  