{"version":3,"file":"iv-spectrum.min.js","sources":["../node_modules/xy-parser/src/index.js","../node_modules/ml-arrayxy-uniquex/src/index.js","../node_modules/ml-xy-convert/src/from.js","../node_modules/ml-xy-convert/src/index.js","../node_modules/ml-xy-convert/src/to.js","../node_modules/convert-to-jcamp/src/creator.js","../node_modules/convert-to-jcamp/src/index.js","../node_modules/jcampconverter/src/index.js","../lib/index.js"],"sourcesContent":["import uniqueXFunction from 'ml-arrayxy-uniquex';\n\n/**\n * Parse a text-file and convert it to an array of XY points\n * @param {string} text - csv or tsv strings\n * @param {object} [options={}]\n * @param {boolean} [options.rescale = false] - will set the maximum value to 1\n * @param {boolean} [options.uniqueX = false] - Make the X values unique (works only with 'xxyy' format). If the X value is repeated the sum of Y is done.\n * @param {number} [options.xColumn = 0] - A number that specifies the x column\n * @param {number} [options.yColumn = 1] - A number that specifies the y column\n * @param {number} [options.maxNumberColumns = (Math.max(xColumn, yColumn)+1)] - A number that specifies the maximum number of y columns\n * @param {number} [options.minNumberColumns = (Math.min(xColumn, yColumn)+1)] - A number that specifies the minimum number of y columns\n * @param {boolean} [options.keepInfo = false] - shoud we keep the non numeric lines. In this case the system will return an object {data, info}\n * @return {object{x:<Array<number>>,y:<Array<number>>}\n */\nexport function parseXY(text, options = {}) {\n  let {\n    rescale = false,\n    uniqueX = false,\n    xColumn = 0,\n    yColumn = 1,\n    keepInfo = false,\n    maxNumberColumns = Number.MAX_SAFE_INTEGER,\n    minNumberColumns = 2,\n  } = options;\n\n  maxNumberColumns = Math.max(maxNumberColumns, xColumn + 1, yColumn + 1);\n  minNumberColumns = Math.max(xColumn + 1, yColumn + 1, minNumberColumns);\n\n  let lines = text.split(/[\\r\\n]+/);\n\n  let maxY = Number.MIN_VALUE;\n  let result = { x: [], y: [] };\n  let info = [];\n  for (let l = 0; l < lines.length; l++) {\n    let line = lines[l].trim();\n    // we will consider only lines that contains only numbers\n    if (line.match(/[0-9]+/) && line.match(/^[0-9eE,;. \\t+-]+$/)) {\n      let fields = line.split(/,[; \\t]+|[; \\t]+/);\n      if (fields.length === 1) {\n        fields = line.split(/[,; \\t]+/);\n      }\n      if (\n        fields &&\n        fields.length >= minNumberColumns &&\n        fields.length <= maxNumberColumns\n      ) {\n        let x = parseFloat(fields[xColumn].replace(',', '.'));\n        let y = parseFloat(fields[yColumn].replace(',', '.'));\n\n        if (y > maxY) maxY = y;\n        result.x.push(x);\n        result.y.push(y);\n      }\n    } else if (line) {\n      info.push({ position: result.x.length, value: line });\n    }\n  }\n\n  if (uniqueX) {\n    uniqueXFunction(result);\n  }\n\n  if (rescale) {\n    for (let i = 0; i < result.y.length; i++) {\n      result.y[i] /= maxY;\n    }\n  }\n\n  if (!keepInfo) return result;\n\n  return {\n    info,\n    data: result,\n  };\n}\n","\n/**\n * In place modification of the 2 arrays to make X unique and sum the Y if X has the same value\n * @param {object} [points={}] : Object of points contains property x (an array) and y (an array)\n * @return points\n */\n\nexport default function uniqueX(points = {}) {\n  const { x, y } = points;\n  if (x.length < 2) return;\n  if (x.length !== y.length) {\n    throw new Error('The X and Y arrays mush have the same length');\n  }\n\n  let current = x[0];\n  let counter = 0;\n\n  for (let i = 1; i < x.length; i++) {\n    if (current !== x[i]) {\n      counter++;\n      current = x[i];\n      x[counter] = x[i];\n      if (i !== counter) {\n        y[counter] = 0;\n      }\n    }\n    if (i !== counter) {\n      y[counter] += y[i];\n    }\n  }\n\n  x.length = counter + 1;\n  y.length = counter + 1;\n}\n","export function fromXxyyArray(data) {\n  return { x: data[0], y: data[1] };\n}\n\nexport function fromXyxyArray(data) {\n  var x = [];\n  var y = [];\n  for (const point of data) {\n    x.push(point[0]);\n    y.push(point[1]);\n  }\n  return { x, y };\n}\n\nexport function fromXyxyObject(data) {\n  var x = [];\n  var y = [];\n  for (const point of data) {\n    x.push(point.x);\n    y.push(point.y);\n  }\n  return { x, y };\n}\n\nexport function fromGeneral(data) {\n  if (Array.isArray(data)) {\n    if (data.length === 0) return { x: [], y: [] };\n    if (Array.isArray(data[0])) {\n      if (data.length === 2) {\n        return fromXxyyArray(data);\n      } else {\n        return fromXyxyArray(data);\n      }\n    } else {\n      return fromXyxyObject(data);\n    }\n  } else {\n    if (Array.isArray(data.x) && Array.isArray(data.x)) {\n      return data;\n    } else {\n      throw new TypeError('unknown data format');\n    }\n  }\n}\n","import { fromGeneral, fromXxyyArray, fromXyxyArray, fromXyxyObject } from './from';\nimport { toXxyyArray, toXyxyArray, toXyxyObject } from './to';\n\n/**\n * Convert between different xy formats\n * @param {*} data - input set of points to parse\n * @param {object} [options] - input and output options\n * @param {string} [options.inputFormat] - input format, if not in list infers the kind\n * @param {string} [options.outputFormat = 'xxyyObject'] - output format\n * @return {*} - output set of points\n */\nexport default function xyConvert(data, options = {}) {\n  const { inputFormat, outputFormat = 'xxyyObject' } = options;\n\n  if (inputFormat === outputFormat) return data;\n\n  let middleData;\n  switch (inputFormat) {\n    case 'xxyyArray':\n      middleData = fromXxyyArray(data);\n      break;\n\n    case 'xyxyArray':\n      middleData = fromXyxyArray(data);\n      break;\n\n    case 'xxyyObject':\n      // this is the base case\n      middleData = data;\n      break;\n\n    case 'xyxyObject':\n      middleData = fromXyxyObject(data);\n      break;\n\n    default:\n      middleData = fromGeneral(data);\n      break;\n  }\n\n  switch (outputFormat) {\n    case 'xxyyArray':\n      return toXxyyArray(middleData);\n\n    case 'xyxyArray':\n      return toXyxyArray(middleData);\n\n    case 'xxyyObject':\n      return middleData;\n\n    case 'xyxyObject':\n      return toXyxyObject(middleData);\n\n    default:\n      throw new TypeError(`unknown output format ${outputFormat}`);\n  }\n}\n","export function toXxyyArray({ x, y }) {\n  return [x, y];\n}\n\nexport function toXyxyArray({ x, y }) {\n  var ans = [];\n  for (var index = 0; index < x.length; index++) {\n    ans.push([x[index], y[index]]);\n  }\n  return ans;\n}\n\nexport function toXyxyObject({ x, y }) {\n  var ans = [];\n  for (var index = 0; index < x.length; index++) {\n    ans.push({ x: x[index], y: y[index] });\n  }\n  return ans;\n}\n","/**\n * Parse from a xyxy data array\n * @param {Array<Array<number>>} data\n * @param {object} [meta] - same metadata object format that the fromText\n * @return {string} JCAMP of the input\n */\nexport default function creator(data, meta = {}) {\n  const {\n    title = '',\n    owner = '',\n    origin = '',\n    type = '',\n    xUnit = '',\n    yUnit = '',\n    info = {},\n  } = meta;\n  let firstX = Number.MAX_VALUE;\n  let lastX = Number.MIN_VALUE;\n  let firstY = Number.MAX_VALUE;\n  let lastY = Number.MIN_VALUE;\n  let points = [];\n\n  for (let i = 0; i < data.x.length; i++) {\n    let x = data.x[i];\n    let y = data.y[i];\n    if (firstX > x) {\n      firstX = x;\n    }\n    if (lastX < x) {\n      lastX = x;\n    }\n    if (firstY > y) {\n      firstY = y;\n    }\n    if (lastY < y) {\n      lastY = y;\n    }\n    points.push(`${x} ${y}`);\n  }\n\n  let header = `##TITLE=${title}\n##JCAMP-DX=4.24\n##DATA TYPE=${type}\n##ORIGIN=${origin}\n##OWNER=${owner}\n##XUNITS=${xUnit}\n##YUNITS=${yUnit}\n##FIRSTX=${firstX}\n##LASTX=${lastX}\n##FIRSTY=${firstY}\n##LASTY=${lastY}\\n`;\n\n  for (const key of Object.keys(info)) {\n    header += `##$${key}=${info[key]}\\n`;\n  }\n\n  // we leave the header and utf8 fonts ${header.replace(/[^\\t\\r\\n\\x20-\\x7F]/g, '')\n\n  return `${header}##NPOINTS=${points.length}\n##PEAK TABLE=(XY..XY)\n${points.join('\\n')}\n##END`;\n}\n","import { parseXY } from 'xy-parser';\nimport xyConvert from 'ml-xy-convert';\n\nimport creator from './creator';\n\n/**\n * Convert strings into JCAMP and add extra information\n * @param {string} data - values to add to the file, usually a csv or tsv values\n * @param {object} [options={}]\n * @param {string} [options.meta] - metadata of the file\n * @param {string} [options.meta.title = ''] - title of the file\n * @param {string} [options.meta.owner = ''] - owner of the file\n * @param {string} [options.meta.origin = ''] - origin of the file\n * @param {string} [options.meta.type = ''] - type of data\n * @param {string} [options.meta.xUnit = ''] - units for the x axis\n * @param {string} [options.meta.yUnit = ''] - units for the y axis\n * @param {object} [options.meta.info = {}] - comments to add to the file\n * @param {object} [options.parser = {}] - 'xy-parser' options. arrayType = 'xyxy' is enforced\n * @return {string} JCAMP of the input\n */\nexport function fromText(data, options = {}) {\n  const { meta = {}, parserOptions = {} } = options;\n\n  parserOptions.keepInfo = true;\n  let parsed = parseXY(data, parserOptions);\n\n  if (!meta.info) meta.info = {};\n  meta.info.header = parsed.info.map((i) => i.value).join('\\n');\n  let jcamp = creator(parsed.data, meta);\n  return jcamp;\n}\n\n/**\n * Parse from any supported format in ml-xy-convert\n * @param {*} data - object or array with a set of points\n * @param {object} [meta] - metadata object\n * @return {string} JCAMP of the input\n */\nexport function fromJSON(data, meta = {}) {\n  const parsed = xyConvert(data, { outputFormat: 'xxyyObject' });\n  return creator(parsed, meta);\n}\n","'use strict';\n\nfunction getConverter() {\n  // the following RegExp can only be used for XYdata, some peakTables have values with a \"E-5\" ...\n  const ntuplesSeparator = /[, \\t]+/;\n  const GC_MS_FIELDS = ['TIC', '.RIC', 'SCANNUMBER'];\n\n  function convertToFloatArray(stringArray) {\n    let floatArray = [];\n    for (let i = 0; i < stringArray.length; i++) {\n      floatArray.push(parseFloat(stringArray[i]));\n    }\n    return floatArray;\n  }\n\n  class Spectrum {}\n\n  const defaultOptions = {\n    keepRecordsRegExp: /^$/,\n    canonicDataLabels: true,\n    dynamicTyping: false,\n    xy: false,\n    withoutXY: false,\n    chromatogram: false,\n    keepSpectra: false,\n    noContour: false,\n    nbContourLevels: 7,\n    noiseMultiplier: 5,\n    profiling: false,\n  };\n\n  function convert(jcamp, options) {\n    options = Object.assign({}, defaultOptions, options);\n\n    let wantXY = !options.withoutXY;\n\n    let start = Date.now();\n\n    let ntuples = {};\n    let ldr, dataValue, ldrs;\n    let position, endLine, infos;\n\n    let result = {};\n    result.profiling = options.profiling ? [] : false;\n    result.logs = [];\n    let spectra = [];\n    result.spectra = spectra;\n    result.info = {};\n    let spectrum = new Spectrum();\n\n    if (!(typeof jcamp === 'string')) {\n      throw new TypeError('the JCAMP should be a string');\n    }\n\n    if (result.profiling) {\n      result.profiling.push({\n        action: 'Before split to LDRS',\n        time: Date.now() - start,\n      });\n    }\n\n    ldrs = jcamp.split(/[\\r\\n]+##/);\n\n    if (result.profiling) {\n      result.profiling.push({\n        action: 'Split to LDRS',\n        time: Date.now() - start,\n      });\n    }\n\n    if (ldrs[0]) ldrs[0] = ldrs[0].replace(/^[\\r\\n ]*##/, '');\n\n    for (let i = 0; i < ldrs.length; i++) {\n      let dataLabel;\n      ldr = ldrs[i];\n      // This is a new LDR\n      position = ldr.indexOf('=');\n      if (position > 0) {\n        dataLabel = ldr.substring(0, position);\n        dataValue = ldr.substring(position + 1).trim();\n      } else {\n        dataLabel = ldr;\n        dataValue = '';\n      }\n      let canonicDataLabel = dataLabel.replace(/[_ -]/g, '').toUpperCase();\n\n      if (canonicDataLabel === 'DATATABLE') {\n        endLine = dataValue.indexOf('\\n');\n        if (endLine === -1) endLine = dataValue.indexOf('\\r');\n        if (endLine > 0) {\n          let xIndex = -1;\n          let yIndex = -1;\n          // ##DATA TABLE= (X++(I..I)), XYDATA\n          // We need to find the variables\n\n          infos = dataValue.substring(0, endLine).split(/[ ,;\\t]+/);\n          if (infos[0].indexOf('++') > 0) {\n            let firstVariable = infos[0].replace(\n              /.*\\(([a-zA-Z0-9]+)\\+\\+.*/,\n              '$1',\n            );\n            let secondVariable = infos[0].replace(\n              /.*\\.\\.([a-zA-Z0-9]+).*/,\n              '$1',\n            );\n            xIndex = ntuples.symbol.indexOf(firstVariable);\n            yIndex = ntuples.symbol.indexOf(secondVariable);\n          }\n\n          if (xIndex === -1) xIndex = 0;\n          if (yIndex === -1) yIndex = 0;\n\n          if (ntuples.first) {\n            if (ntuples.first.length > xIndex) {\n              spectrum.firstX = ntuples.first[xIndex];\n            }\n            if (ntuples.first.length > yIndex) {\n              spectrum.firstY = ntuples.first[yIndex];\n            }\n          }\n          if (ntuples.last) {\n            if (ntuples.last.length > xIndex) {\n              spectrum.lastX = ntuples.last[xIndex];\n            }\n            if (ntuples.last.length > yIndex) {\n              spectrum.lastY = ntuples.last[yIndex];\n            }\n          }\n          if (ntuples.vardim && ntuples.vardim.length > xIndex) {\n            spectrum.nbPoints = ntuples.vardim[xIndex];\n          }\n          if (ntuples.factor) {\n            if (ntuples.factor.length > xIndex) {\n              spectrum.xFactor = ntuples.factor[xIndex];\n            }\n            if (ntuples.factor.length > yIndex) {\n              spectrum.yFactor = ntuples.factor[yIndex];\n            }\n          }\n          if (ntuples.units) {\n            if (ntuples.units.length > xIndex) {\n              spectrum.xUnit = ntuples.units[xIndex];\n            }\n            if (ntuples.units.length > yIndex) {\n              spectrum.yUnit = ntuples.units[yIndex];\n            }\n          }\n          spectrum.datatable = infos[0];\n          if (infos[1] && infos[1].indexOf('PEAKS') > -1) {\n            canonicDataLabel = 'PEAKTABLE';\n          } else if (\n            infos[1] &&\n            (infos[1].indexOf('XYDATA') || infos[0].indexOf('++') > 0)\n          ) {\n            canonicDataLabel = 'XYDATA';\n            spectrum.deltaX =\n              (spectrum.lastX - spectrum.firstX) / (spectrum.nbPoints - 1);\n          }\n        }\n      }\n\n      if (canonicDataLabel === 'XYDATA') {\n        if (wantXY) {\n          prepareSpectrum(result, spectrum);\n          // well apparently we should still consider it is a PEAK TABLE if there are no '++' after\n          if (dataValue.match(/.*\\+\\+.*/)) {\n            // ex: (X++(Y..Y))\n            if (!spectrum.deltaX) {\n              spectrum.deltaX =\n                (spectrum.lastX - spectrum.firstX) / (spectrum.nbPoints - 1);\n            }\n            fastParseXYData(spectrum, dataValue, result);\n          } else {\n            parsePeakTable(spectrum, dataValue, result);\n          }\n          spectra.push(spectrum);\n          spectrum = new Spectrum();\n        }\n        continue;\n      } else if (canonicDataLabel === 'PEAKTABLE') {\n        if (wantXY) {\n          prepareSpectrum(result, spectrum);\n          parsePeakTable(spectrum, dataValue, result);\n          spectra.push(spectrum);\n          spectrum = new Spectrum();\n        }\n        continue;\n      }\n      if (canonicDataLabel === 'PEAKASSIGNMENTS') {\n        if (wantXY) {\n          if (dataValue.match(/.*(XYA).*/)) {\n            // ex: (XYA)\n            parseXYA(spectrum, dataValue);\n          }\n          spectra.push(spectrum);\n          spectrum = new Spectrum();\n        }\n        continue;\n      }\n\n      if (canonicDataLabel === 'TITLE') {\n        spectrum.title = dataValue;\n      } else if (canonicDataLabel === 'DATATYPE') {\n        spectrum.dataType = dataValue;\n        if (dataValue.indexOf('nD') > -1) {\n          result.twoD = true;\n        }\n      } else if (canonicDataLabel === 'NTUPLES') {\n        if (dataValue.indexOf('nD') > -1) {\n          result.twoD = true;\n        }\n      } else if (canonicDataLabel === 'XUNITS') {\n        spectrum.xUnit = dataValue;\n      } else if (canonicDataLabel === 'YUNITS') {\n        spectrum.yUnit = dataValue;\n      } else if (canonicDataLabel === 'FIRSTX') {\n        spectrum.firstX = parseFloat(dataValue);\n      } else if (canonicDataLabel === 'LASTX') {\n        spectrum.lastX = parseFloat(dataValue);\n      } else if (canonicDataLabel === 'FIRSTY') {\n        spectrum.firstY = parseFloat(dataValue);\n      } else if (canonicDataLabel === 'LASTY') {\n        spectrum.lastY = parseFloat(dataValue);\n      } else if (canonicDataLabel === 'NPOINTS') {\n        spectrum.nbPoints = parseFloat(dataValue);\n      } else if (canonicDataLabel === 'XFACTOR') {\n        spectrum.xFactor = parseFloat(dataValue);\n      } else if (canonicDataLabel === 'YFACTOR') {\n        spectrum.yFactor = parseFloat(dataValue);\n      } else if (canonicDataLabel === 'MAXX') {\n        spectrum.maxX = parseFloat(dataValue);\n      } else if (canonicDataLabel === 'MINX') {\n        spectrum.minX = parseFloat(dataValue);\n      } else if (canonicDataLabel === 'MAXY') {\n        spectrum.maxY = parseFloat(dataValue);\n      } else if (canonicDataLabel === 'MINY') {\n        spectrum.minY = parseFloat(dataValue);\n      } else if (canonicDataLabel === 'DELTAX') {\n        spectrum.deltaX = parseFloat(dataValue);\n      } else if (\n        canonicDataLabel === '.OBSERVEFREQUENCY' ||\n        canonicDataLabel === '$SFO1'\n      ) {\n        if (!spectrum.observeFrequency) {\n          spectrum.observeFrequency = parseFloat(dataValue);\n        }\n      } else if (canonicDataLabel === '.OBSERVENUCLEUS') {\n        if (!spectrum.xType) {\n          result.xType = dataValue.replace(/[^a-zA-Z0-9]/g, '');\n        }\n      } else if (canonicDataLabel === '$SFO2') {\n        if (!result.indirectFrequency) {\n          result.indirectFrequency = parseFloat(dataValue);\n        }\n      } else if (canonicDataLabel === '$OFFSET') {\n        // OFFSET for Bruker spectra\n        result.shiftOffsetNum = 0;\n        if (!spectrum.shiftOffsetVal) {\n          spectrum.shiftOffsetVal = parseFloat(dataValue);\n        }\n      } else if (canonicDataLabel === '$REFERENCEPOINT') {\n        // OFFSET for Varian spectra\n        // if we activate this part it does not work for ACD specmanager\n        //         } else if (canonicDataLabel=='.SHIFTREFERENCE') {   // OFFSET FOR Bruker Spectra\n        //                 var parts = dataValue.split(/ *, */);\n        //                 result.shiftOffsetNum = parseInt(parts[2].trim());\n        //                 spectrum.shiftOffsetVal = parseFloat(parts[3].trim());\n      } else if (canonicDataLabel === 'VARNAME') {\n        ntuples.varname = dataValue.split(ntuplesSeparator);\n      } else if (canonicDataLabel === 'SYMBOL') {\n        ntuples.symbol = dataValue.split(ntuplesSeparator);\n      } else if (canonicDataLabel === 'VARTYPE') {\n        ntuples.vartype = dataValue.split(ntuplesSeparator);\n      } else if (canonicDataLabel === 'VARFORM') {\n        ntuples.varform = dataValue.split(ntuplesSeparator);\n      } else if (canonicDataLabel === 'VARDIM') {\n        ntuples.vardim = convertToFloatArray(dataValue.split(ntuplesSeparator));\n      } else if (canonicDataLabel === 'UNITS') {\n        ntuples.units = dataValue.split(ntuplesSeparator);\n      } else if (canonicDataLabel === 'FACTOR') {\n        ntuples.factor = convertToFloatArray(dataValue.split(ntuplesSeparator));\n      } else if (canonicDataLabel === 'FIRST') {\n        ntuples.first = convertToFloatArray(dataValue.split(ntuplesSeparator));\n      } else if (canonicDataLabel === 'LAST') {\n        ntuples.last = convertToFloatArray(dataValue.split(ntuplesSeparator));\n      } else if (canonicDataLabel === 'MIN') {\n        ntuples.min = convertToFloatArray(dataValue.split(ntuplesSeparator));\n      } else if (canonicDataLabel === 'MAX') {\n        ntuples.max = convertToFloatArray(dataValue.split(ntuplesSeparator));\n      } else if (canonicDataLabel === '.NUCLEUS') {\n        if (result.twoD) {\n          result.yType = dataValue.split(ntuplesSeparator)[0];\n        }\n      } else if (canonicDataLabel === 'PAGE') {\n        spectrum.page = dataValue.trim();\n        spectrum.pageValue = parseFloat(dataValue.replace(/^.*=/, ''));\n        spectrum.pageSymbol = spectrum.page.replace(/[=].*/, '');\n        let pageSymbolIndex = ntuples.symbol.indexOf(spectrum.pageSymbol);\n        let unit = '';\n        if (ntuples.units && ntuples.units[pageSymbolIndex]) {\n          unit = ntuples.units[pageSymbolIndex];\n        }\n        if (result.indirectFrequency && unit !== 'PPM') {\n          spectrum.pageValue /= result.indirectFrequency;\n        }\n      } else if (canonicDataLabel === 'RETENTIONTIME') {\n        spectrum.pageValue = parseFloat(dataValue);\n      } else if (isMSField(canonicDataLabel)) {\n        spectrum[convertMSFieldToLabel(canonicDataLabel)] = dataValue;\n      } else if (canonicDataLabel === 'SAMPLEDESCRIPTION') {\n        spectrum.sampleDescription = dataValue;\n      }\n      if (canonicDataLabel.match(options.keepRecordsRegExp)) {\n        let label = options.canonicDataLabels ? canonicDataLabel : dataLabel;\n        let value = dataValue.trim();\n        if (options.dynamicTyping && !isNaN(value)) {\n          value = Number(value);\n        }\n        if (result.info[label]) {\n          if (!Array.isArray(result.info[label])) {\n            result.info[label] = [result.info[label]];\n          }\n          result.info[label].push(value);\n        } else {\n          result.info[label] = value;\n        }\n      }\n    }\n\n    if (result.profiling) {\n      result.profiling.push({\n        action: 'Finished parsing',\n        time: Date.now() - start,\n      });\n    }\n\n    if (Object.keys(ntuples).length > 0) {\n      let newNtuples = [];\n      let keys = Object.keys(ntuples);\n      for (let i = 0; i < keys.length; i++) {\n        let key = keys[i];\n        let values = ntuples[key];\n        for (let j = 0; j < values.length; j++) {\n          if (!newNtuples[j]) newNtuples[j] = {};\n          newNtuples[j][key] = values[j];\n        }\n      }\n      result.ntuples = newNtuples;\n    }\n\n    if (result.twoD && wantXY) {\n      add2D(result, options);\n      if (result.profiling) {\n        result.profiling.push({\n          action: 'Finished countour plot calculation',\n          time: Date.now() - start,\n        });\n      }\n      if (!options.keepSpectra) {\n        delete result.spectra;\n      }\n    }\n\n    if (options.chromatogram) {\n      options.xy = true;\n    }\n\n    if (options.xy && wantXY) {\n      // the spectraData should not be a oneD array but an object with x and y\n      if (spectra.length > 0) {\n        for (let i = 0; i < spectra.length; i++) {\n          spectrum = spectra[i];\n          if (spectrum.data.length > 0) {\n            for (let j = 0; j < spectrum.data.length; j++) {\n              let data = spectrum.data[j];\n              let newData = {\n                x: new Array(data.length / 2),\n                y: new Array(data.length / 2),\n              };\n              for (let k = 0; k < data.length; k = k + 2) {\n                newData.x[k / 2] = data[k];\n                newData.y[k / 2] = data[k + 1];\n              }\n              spectrum.data[j] = newData;\n            }\n          }\n        }\n      }\n    }\n\n    // maybe it is a GC (HPLC) / MS. In this case we add a new format\n    if (options.chromatogram) {\n      if (result.spectra.length > 1) {\n        complexChromatogram(result);\n      } else {\n        simpleChromatogram(result);\n      }\n      if (result.profiling) {\n        result.profiling.push({\n          action: 'Finished chromatogram calculation',\n          time: Date.now() - start,\n        });\n      }\n    }\n\n    if (result.profiling) {\n      result.profiling.push({\n        action: 'Total time',\n        time: Date.now() - start,\n      });\n    }\n\n    return result;\n  }\n\n  function convertMSFieldToLabel(value) {\n    return value.toLowerCase().replace(/[^a-z0-9]/g, '');\n  }\n\n  function isMSField(canonicDataLabel) {\n    return GC_MS_FIELDS.indexOf(canonicDataLabel) !== -1;\n  }\n\n  function complexChromatogram(result) {\n    let spectra = result.spectra;\n    let length = spectra.length;\n    let chromatogram = {\n      times: new Array(length),\n      series: {\n        ms: {\n          dimension: 2,\n          data: new Array(length),\n        },\n      },\n    };\n\n    let existingGCMSFields = [];\n    for (let i = 0; i < GC_MS_FIELDS.length; i++) {\n      let label = convertMSFieldToLabel(GC_MS_FIELDS[i]);\n      if (spectra[0][label]) {\n        existingGCMSFields.push(label);\n        chromatogram.series[label] = {\n          dimension: 1,\n          data: new Array(length),\n        };\n      }\n    }\n\n    for (let i = 0; i < length; i++) {\n      let spectrum = spectra[i];\n      chromatogram.times[i] = spectrum.pageValue;\n      for (let j = 0; j < existingGCMSFields.length; j++) {\n        chromatogram.series[existingGCMSFields[j]].data[i] = parseFloat(\n          spectrum[existingGCMSFields[j]],\n        );\n      }\n      if (spectrum.data) {\n        chromatogram.series.ms.data[i] = [\n          spectrum.data[0].x,\n          spectrum.data[0].y,\n        ];\n      }\n    }\n    result.chromatogram = chromatogram;\n  }\n\n  function simpleChromatogram(result) {\n    let data = result.spectra[0].data[0];\n    result.chromatogram = {\n      times: data.x.slice(),\n      series: {\n        intensity: {\n          dimension: 1,\n          data: data.y.slice(),\n        },\n      },\n    };\n  }\n\n  function prepareSpectrum(result, spectrum) {\n    if (!spectrum.xFactor) spectrum.xFactor = 1;\n    if (!spectrum.yFactor) spectrum.yFactor = 1;\n    if (spectrum.observeFrequency) {\n      if (spectrum.xUnit && spectrum.xUnit.toUpperCase() === 'HZ') {\n        spectrum.xUnit = 'PPM';\n        spectrum.xFactor = spectrum.xFactor / spectrum.observeFrequency;\n        spectrum.firstX = spectrum.firstX / spectrum.observeFrequency;\n        spectrum.lastX = spectrum.lastX / spectrum.observeFrequency;\n        spectrum.deltaX = spectrum.deltaX / spectrum.observeFrequency;\n      }\n    }\n    if (spectrum.shiftOffsetVal) {\n      let shift = spectrum.firstX - spectrum.shiftOffsetVal;\n      spectrum.firstX = spectrum.firstX - shift;\n      spectrum.lastX = spectrum.lastX - shift;\n    }\n  }\n\n  function getMedian(data) {\n    data = data.sort(compareNumbers);\n    let l = data.length;\n    return data[Math.floor(l / 2)];\n  }\n\n  function compareNumbers(a, b) {\n    return a - b;\n  }\n\n  function convertTo3DZ(spectra) {\n    let minZ = spectra[0].data[0][0];\n    let maxZ = minZ;\n    let ySize = spectra.length;\n    let xSize = spectra[0].data[0].length / 2;\n    let z = new Array(ySize);\n    for (let i = 0; i < ySize; i++) {\n      z[i] = new Array(xSize);\n      let xVector = spectra[i].data[0];\n      for (let j = 0; j < xSize; j++) {\n        let value = xVector[j * 2 + 1];\n        z[i][j] = value;\n        if (value < minZ) minZ = value;\n        if (value > maxZ) maxZ = value;\n      }\n    }\n\n    const firstX = spectra[0].data[0][0];\n    const lastX = spectra[0].data[0][spectra[0].data[0].length - 2]; // has to be -2 because it is a 1D array [x,y,x,y,...]\n    const firstY = spectra[0].pageValue;\n    const lastY = spectra[ySize - 1].pageValue;\n\n    // Because the min / max value are the only information about the matrix if we invert\n    // min and max we need to invert the array\n    if (firstX > lastX) {\n      for (let spectrum of z) {\n        spectrum.reverse();\n      }\n    }\n    if (firstY > lastY) {\n      z.reverse();\n    }\n\n    return {\n      z: z,\n      minX: Math.min(firstX, lastX),\n      maxX: Math.max(firstX, lastX),\n      minY: Math.min(firstY, lastY),\n      maxY: Math.max(firstY, lastY),\n      minZ: minZ,\n      maxZ: maxZ,\n      noise: getMedian(z[0].map(Math.abs)),\n    };\n  }\n\n  function add2D(result, options) {\n    let zData = convertTo3DZ(result.spectra);\n    if (!options.noContour) {\n      result.contourLines = generateContourLines(zData, options);\n      delete zData.z;\n    }\n    result.minMax = zData;\n  }\n\n  function generateContourLines(zData, options) {\n    let noise = zData.noise;\n    let z = zData.z;\n    let povarHeight0, povarHeight1, povarHeight2, povarHeight3;\n    let isOver0, isOver1, isOver2, isOver3;\n    let nbSubSpectra = z.length;\n    let nbPovars = z[0].length;\n    let pAx, pAy, pBx, pBy;\n\n    let x0 = zData.minX;\n    let xN = zData.maxX;\n    let dx = (xN - x0) / (nbPovars - 1);\n    let y0 = zData.minY;\n    let yN = zData.maxY;\n    let dy = (yN - y0) / (nbSubSpectra - 1);\n    let minZ = zData.minZ;\n    let maxZ = zData.maxZ;\n\n    // System.out.prvarln('y0 '+y0+' yN '+yN);\n    // -------------------------\n    // Povars attribution\n    //\n    // 0----1\n    // |  / |\n    // | /  |\n    // 2----3\n    //\n    // ---------------------d------\n\n    let iter = options.nbContourLevels * 2;\n    let contourLevels = new Array(iter);\n    let lineZValue;\n    for (let level = 0; level < iter; level++) {\n      // multiply by 2 for positif and negatif\n      let contourLevel = {};\n      contourLevels[level] = contourLevel;\n      let side = level % 2;\n      let factor =\n        (maxZ - options.noiseMultiplier * noise) *\n        Math.exp((level >> 1) - options.nbContourLevels);\n      if (side === 0) {\n        lineZValue = factor + options.noiseMultiplier * noise;\n      } else {\n        lineZValue = 0 - factor - options.noiseMultiplier * noise;\n      }\n      let lines = [];\n      contourLevel.zValue = lineZValue;\n      contourLevel.lines = lines;\n\n      if (lineZValue <= minZ || lineZValue >= maxZ) continue;\n\n      for (let iSubSpectra = 0; iSubSpectra < nbSubSpectra - 1; iSubSpectra++) {\n        let subSpectra = z[iSubSpectra];\n        let subSpectraAfter = z[iSubSpectra + 1];\n        for (let povar = 0; povar < nbPovars - 1; povar++) {\n          povarHeight0 = subSpectra[povar];\n          povarHeight1 = subSpectra[povar + 1];\n          povarHeight2 = subSpectraAfter[povar];\n          povarHeight3 = subSpectraAfter[povar + 1];\n\n          isOver0 = povarHeight0 > lineZValue;\n          isOver1 = povarHeight1 > lineZValue;\n          isOver2 = povarHeight2 > lineZValue;\n          isOver3 = povarHeight3 > lineZValue;\n\n          // Example povar0 is over the plane and povar1 and\n          // povar2 are below, we find the varersections and add\n          // the segment\n          if (isOver0 !== isOver1 && isOver0 !== isOver2) {\n            pAx =\n              povar +\n              (lineZValue - povarHeight0) / (povarHeight1 - povarHeight0);\n            pAy = iSubSpectra;\n            pBx = povar;\n            pBy =\n              iSubSpectra +\n              (lineZValue - povarHeight0) / (povarHeight2 - povarHeight0);\n            lines.push(pAx * dx + x0);\n            lines.push(pAy * dy + y0);\n            lines.push(pBx * dx + x0);\n            lines.push(pBy * dy + y0);\n          }\n          // remove push does not help !!!!\n          if (isOver3 !== isOver1 && isOver3 !== isOver2) {\n            pAx = povar + 1;\n            pAy =\n              iSubSpectra +\n              1 -\n              (lineZValue - povarHeight3) / (povarHeight1 - povarHeight3);\n            pBx =\n              povar +\n              1 -\n              (lineZValue - povarHeight3) / (povarHeight2 - povarHeight3);\n            pBy = iSubSpectra + 1;\n            lines.push(pAx * dx + x0);\n            lines.push(pAy * dy + y0);\n            lines.push(pBx * dx + x0);\n            lines.push(pBy * dy + y0);\n          }\n          // test around the diagonal\n          if (isOver1 !== isOver2) {\n            pAx =\n              (povar +\n                1 -\n                (lineZValue - povarHeight1) / (povarHeight2 - povarHeight1)) *\n                dx +\n              x0;\n            pAy =\n              (iSubSpectra +\n                (lineZValue - povarHeight1) / (povarHeight2 - povarHeight1)) *\n                dy +\n              y0;\n            if (isOver1 !== isOver0) {\n              pBx =\n                povar +\n                1 -\n                (lineZValue - povarHeight1) / (povarHeight0 - povarHeight1);\n              pBy = iSubSpectra;\n              lines.push(pAx);\n              lines.push(pAy);\n              lines.push(pBx * dx + x0);\n              lines.push(pBy * dy + y0);\n            }\n            if (isOver2 !== isOver0) {\n              pBx = povar;\n              pBy =\n                iSubSpectra +\n                1 -\n                (lineZValue - povarHeight2) / (povarHeight0 - povarHeight2);\n              lines.push(pAx);\n              lines.push(pAy);\n              lines.push(pBx * dx + x0);\n              lines.push(pBy * dy + y0);\n            }\n            if (isOver1 !== isOver3) {\n              pBx = povar + 1;\n              pBy =\n                iSubSpectra +\n                (lineZValue - povarHeight1) / (povarHeight3 - povarHeight1);\n              lines.push(pAx);\n              lines.push(pAy);\n              lines.push(pBx * dx + x0);\n              lines.push(pBy * dy + y0);\n            }\n            if (isOver2 !== isOver3) {\n              pBx =\n                povar +\n                (lineZValue - povarHeight2) / (povarHeight3 - povarHeight2);\n              pBy = iSubSpectra + 1;\n              lines.push(pAx);\n              lines.push(pAy);\n              lines.push(pBx * dx + x0);\n              lines.push(pBy * dy + y0);\n            }\n          }\n        }\n      }\n    }\n\n    return {\n      minX: zData.minX,\n      maxX: zData.maxX,\n      minY: zData.minY,\n      maxY: zData.maxY,\n      segments: contourLevels,\n    };\n  }\n\n  function fastParseXYData(spectrum, value) {\n    // TODO need to deal with result\n    //  console.log(value);\n    // we check if deltaX is defined otherwise we calculate it\n\n    let yFactor = spectrum.yFactor;\n    let deltaX = spectrum.deltaX;\n\n    spectrum.isXYdata = true;\n    // TODO to be improved using 2 array {x:[], y:[]}\n    let currentData = [];\n    spectrum.data = [currentData];\n\n    let currentX = spectrum.firstX;\n    let currentY = spectrum.firstY;\n\n    // we skip the first line\n    //\n    let endLine = false;\n    let ascii;\n    let i = 0;\n    for (; i < value.length; i++) {\n      ascii = value.charCodeAt(i);\n      if (ascii === 13 || ascii === 10) {\n        endLine = true;\n      } else {\n        if (endLine) break;\n      }\n    }\n\n    // we proceed taking the i after the first line\n    let newLine = true;\n    let isDifference = false;\n    let isLastDifference = false;\n    let lastDifference = 0;\n    let isDuplicate = false;\n    let inComment = false;\n    let currentValue = 0; // can be a difference or a duplicate\n    let lastValue = 0; // must be the real last value\n    let isNegative = false;\n    let inValue = false;\n    let skipFirstValue = false;\n    let decimalPosition = 0;\n    for (; i <= value.length; i++) {\n      if (i === value.length) ascii = 13;\n      else ascii = value.charCodeAt(i);\n      if (inComment) {\n        // we should ignore the text if we are after $$\n        if (ascii === 13 || ascii === 10) {\n          newLine = true;\n          inComment = false;\n        }\n      } else {\n        // when is it a new value ?\n        // when it is not a digit, . or comma\n        // it is a number that is either new or we continue\n        if (ascii <= 57 && ascii >= 48) {\n          // a number\n          inValue = true;\n          if (decimalPosition > 0) {\n            currentValue += (ascii - 48) / Math.pow(10, decimalPosition++);\n          } else {\n            currentValue *= 10;\n            currentValue += ascii - 48;\n          }\n        } else if (ascii === 44 || ascii === 46) {\n          // a \",\" or \".\"\n          inValue = true;\n          decimalPosition++;\n        } else {\n          if (inValue) {\n            // need to process the previous value\n            if (newLine) {\n              newLine = false; // we don't check the X value\n              // console.log(\"NEW LINE\",isDifference, lastDifference);\n              // if new line and lastDifference, the first value is just a check !\n              // that we don't check ...\n              if (isLastDifference) skipFirstValue = true;\n            } else {\n              // need to deal with duplicate and differences\n              if (skipFirstValue) {\n                skipFirstValue = false;\n              } else {\n                if (isDifference) {\n                  lastDifference = isNegative ? 0 - currentValue : currentValue;\n                  isLastDifference = true;\n                  isDifference = false;\n                } else if (!isDuplicate) {\n                  lastValue = isNegative ? 0 - currentValue : currentValue;\n                }\n                let duplicate = isDuplicate ? currentValue - 1 : 1;\n                for (let j = 0; j < duplicate; j++) {\n                  if (isLastDifference) {\n                    currentY += lastDifference;\n                  } else {\n                    currentY = lastValue;\n                  }\n                  currentData.push(currentX);\n                  currentData.push(currentY * yFactor);\n                  currentX += deltaX;\n                }\n              }\n            }\n            isNegative = false;\n            currentValue = 0;\n            decimalPosition = 0;\n            inValue = false;\n            isDuplicate = false;\n          }\n\n          // positive SQZ digits @ A B C D E F G H I (ascii 64-73)\n          if (ascii < 74 && ascii > 63) {\n            inValue = true;\n            isLastDifference = false;\n            currentValue = ascii - 64;\n          } else if (ascii > 96 && ascii < 106) {\n            // negative SQZ digits a b c d e f g h i (ascii 97-105)\n            inValue = true;\n            isLastDifference = false;\n            currentValue = ascii - 96;\n            isNegative = true;\n          } else if (ascii === 115) {\n            // DUP digits S T U V W X Y Z s (ascii 83-90, 115)\n            inValue = true;\n            isDuplicate = true;\n            currentValue = 9;\n          } else if (ascii > 82 && ascii < 91) {\n            inValue = true;\n            isDuplicate = true;\n            currentValue = ascii - 82;\n          } else if (ascii > 73 && ascii < 83) {\n            // positive DIF digits % J K L M N O P Q R (ascii 37, 74-82)\n            inValue = true;\n            isDifference = true;\n            currentValue = ascii - 73;\n          } else if (ascii > 105 && ascii < 115) {\n            // negative DIF digits j k l m n o p q r (ascii 106-114)\n            inValue = true;\n            isDifference = true;\n            currentValue = ascii - 105;\n            isNegative = true;\n          } else if (ascii === 36 && value.charCodeAt(i + 1) === 36) {\n            // $ sign, we need to check the next one\n            inValue = true;\n            inComment = true;\n          } else if (ascii === 37) {\n            // positive DIF digits % J K L M N O P Q R (ascii 37, 74-82)\n            inValue = true;\n            isDifference = true;\n            currentValue = 0;\n            isNegative = false;\n          } else if (ascii === 45) {\n            // a \"-\"\n            // check if after there is a number, decimal or comma\n            let ascii2 = value.charCodeAt(i + 1);\n            if (\n              (ascii2 >= 48 && ascii2 <= 57) ||\n              ascii2 === 44 ||\n              ascii2 === 46\n            ) {\n              inValue = true;\n              if (!newLine) isLastDifference = false;\n              isNegative = true;\n            }\n          } else if (ascii === 13 || ascii === 10) {\n            newLine = true;\n            inComment = false;\n          }\n          // and now analyse the details ... space or tabulation\n          // if \"+\" we just don't care\n        }\n      }\n    }\n  }\n\n  function parseXYA(spectrum, value) {\n    let removeSymbolRegExp = /(\\(+|\\)+|<+|>+|\\s+)/g;\n\n    spectrum.isXYAdata = true;\n    let values;\n    let currentData = [];\n    spectrum.data = [currentData];\n\n    let lines = value.split(/,? *,?[;\\r\\n]+ */);\n\n    for (let i = 1; i < lines.length; i++) {\n      values = lines[i]\n        .trim()\n        .replace(removeSymbolRegExp, '')\n        .split(',');\n      currentData.push(parseFloat(values[0]));\n      currentData.push(parseFloat(values[1]));\n    }\n  }\n\n  function parsePeakTable(spectrum, value, result) {\n    let removeCommentRegExp = /\\$\\$.*/;\n    let peakTableSplitRegExp = /[,\\t ]+/;\n\n    spectrum.isPeaktable = true;\n    let values;\n    let currentData = [];\n    spectrum.data = [currentData];\n\n    // counts for around 20% of the time\n    let lines = value.split(/,? *,?[;\\r\\n]+ */);\n\n    for (let i = 1; i < lines.length; i++) {\n      values = lines[i]\n        .trim()\n        .replace(removeCommentRegExp, '')\n        .split(peakTableSplitRegExp);\n      if (values.length % 2 === 0) {\n        for (let j = 0; j < values.length; j = j + 2) {\n          // takes around 40% of the time to add and parse the 2 values nearly exclusively because of parseFloat\n          currentData.push(parseFloat(values[j]) * spectrum.xFactor);\n          currentData.push(parseFloat(values[j + 1]) * spectrum.yFactor);\n        }\n      } else {\n        result.logs.push(`Format error: ${values}`);\n      }\n    }\n  }\n\n  return convert;\n}\n\nlet convert = getConverter();\n\nfunction JcampConverter(input, options, useWorker) {\n  if (typeof options === 'boolean') {\n    useWorker = options;\n    options = {};\n  }\n  if (useWorker) {\n    return postToWorker(input, options);\n  } else {\n    return convert(input, options);\n  }\n}\n\nlet stamps = {};\nlet worker;\n\nfunction postToWorker(input, options) {\n  if (!worker) {\n    createWorker();\n  }\n  return new Promise(function(resolve) {\n    let stamp = `${Date.now()}${Math.random()}`;\n    stamps[stamp] = resolve;\n    worker.postMessage(\n      JSON.stringify({\n        stamp: stamp,\n        input: input,\n        options: options,\n      }),\n    );\n  });\n}\n\nfunction createWorker() {\n  let workerURL = URL.createObjectURL(\n    new Blob(\n      [\n        `var getConverter =${getConverter.toString()};var convert = getConverter(); onmessage = function (event) { var data = JSON.parse(event.data); postMessage(JSON.stringify({stamp: data.stamp, output: convert(data.input, data.options)})); };`,\n      ],\n      { type: 'application/javascript' },\n    ),\n  );\n  worker = new Worker(workerURL);\n  URL.revokeObjectURL(workerURL);\n  worker.addEventListener('message', function(event) {\n    let data = JSON.parse(event.data);\n    let stamp = data.stamp;\n    if (stamps[stamp]) {\n      stamps[stamp](data.output);\n    }\n  });\n}\n\nfunction createTree(jcamp, options = {}) {\n  const { flatten = false } = options;\n  if (typeof jcamp !== 'string') {\n    throw new TypeError('the JCAMP should be a string');\n  }\n\n  let lines = jcamp.split(/[\\r\\n]+/);\n  let flat = [];\n  let stack = [];\n  let result = [];\n  let current;\n  let ntupleLevel = 0;\n\n  let spaces = jcamp.includes('## ');\n\n  for (let i = 0; i < lines.length; i++) {\n    let line = lines[i];\n    let labelLine = spaces ? line.replace(/ /g, '') : line;\n\n    if (labelLine.substring(0, 9) === '##NTUPLES') {\n      ntupleLevel++;\n    }\n\n    if (labelLine.substring(0, 7) === '##TITLE') {\n      let title = [labelLine.substring(8).trim()];\n      for (let j = i + 1; j < lines.length; j++) {\n        if (lines[j].startsWith('##')) {\n          break;\n        } else {\n          title.push(lines[j].trim());\n        }\n      }\n      stack.push({\n        title: title.join('\\n'),\n        jcamp: `${line}\\n`,\n        children: [],\n      });\n      current = stack[stack.length - 1];\n      flat.push(current);\n    } else if (labelLine.substring(0, 5) === '##END' && ntupleLevel === 0) {\n      current.jcamp += `${line}\\n`;\n      let finished = stack.pop();\n      if (stack.length !== 0) {\n        current = stack[stack.length - 1];\n        current.children.push(finished);\n      } else {\n        current = undefined;\n        result.push(finished);\n      }\n    } else if (current && current.jcamp) {\n      current.jcamp += `${line}\\n`;\n      let match = labelLine.match(/^##(.*?)=(.+)/);\n      if (match) {\n        let canonicDataLabel = match[1].replace(/[ _-]/g, '').toUpperCase();\n        if (canonicDataLabel === 'DATATYPE') {\n          current.dataType = match[2].trim();\n        }\n      }\n    }\n\n    if (labelLine.substring(0, 5) === '##END' && ntupleLevel > 0) {\n      ntupleLevel--;\n    }\n  }\n  if (flatten) {\n    flat.forEach((entry) => {\n      entry.children = undefined;\n    });\n    return flat;\n  } else {\n    return result;\n  }\n}\n\nmodule.exports = {\n  convert: JcampConverter,\n  createTree: createTree,\n};\n","'use strict';\n\nObject.defineProperty(exports, '__esModule', { value: true });\n\nvar convertToJcamp = require('convert-to-jcamp');\nvar jcampconverter = require('jcampconverter');\n\nfunction toJcamp(spectrum) {\n  let meta = {\n    // title: spectrum.sampleMeta.cellname,\n    owner: '',\n    origin: '',\n    type: 'IV curve',\n    xUnit: 'difference in electric potential [V]',\n    yUnit: 'intensity [A]',\n    info: spectrum.meta,\n  };\n\n  return convertToJcamp.fromJSON({ x: spectrum.x, y: spectrum.y }, meta);\n}\n\n/**\n * Class allowing manipulate one UV spectrum\n * @class spectrum\n * @param {object} [data={}] - object containing a spectrum\n * @param {Array} [data.x=[]] - voltage\n * @param {Array} [data.y=[]] - intensity\n */\nclass Spectrum {\n  constructor(x, y, id, options = {}) {\n    const { meta = {} } = options;\n    if (x && x.length > 1 && x[0] > x[1]) {\n      this.x = x.reverse();\n      this.y = y.reverse();\n    } else {\n      this.x = x || [];\n      this.y = y || [];\n    }\n\n    this.id = id;\n    this.meta = meta;\n  }\n\n  getXLabel() {\n    return 'Voltage [V]';\n  }\n\n  getYLabel() {\n    return 'Intensity [A]';\n  }\n}\n\nSpectrum.prototype.getData = function() {\n  return { x: this.x, y: this.y };\n};\n\nSpectrum.prototype.toJcamp = function() {\n  return toJcamp(this);\n};\n\nfunction fromSIV(content) {\n  let allLines = content.split(/[\\r\\n]+/);\n  let sampleMeta = parseS(allLines.filter((line) => line.match(/X S_/)));\n  let instrumentMeta = parseV(allLines.filter((line) => line.match(/X V_/)));\n  let date = parseDate(allLines.filter((line) => line.match(/X d_t/))[0]);\n\n  let parts = content.split('WAVES\\t');\n  let spectra = [];\n\n  for (let part of parts) {\n    let lines = part.split(/[\\r\\n]+/);\n    let ys = lines\n      .filter((line) => line.match(/^[\\t 0-9.eE-]+$/))\n      .map((line) => Number(line));\n    if (ys.length < 10) continue;\n\n    let kind = lines[0].trim();\n    let metaLines = lines\n      .filter((line) => line.match(/^X /))\n      .map((line) => line.substring(2));\n\n    let axis = parseScale(metaLines[0], ys.length);\n\n    if (axis.x === undefined || axis.x.unit !== 'V') {\n      // eslint-disable-next-line no-console\n      console.log('Unknown X axis:', axis.kind, axis.unit);\n      continue;\n    }\n    if (axis.y === undefined || axis.y.unit !== 'A') {\n      // eslint-disable-next-line no-console\n      console.log('Unknown Y axis:', axis.kind, axis.unit);\n      continue;\n    }\n    // let note = parseNote(metaLines[1]);\n    let xs = axis.x.values;\n    let data = {\n      x: xs,\n      y: ys,\n    };\n\n    let meta = {\n      ...sampleMeta,\n      date,\n      experiment: kind,\n      ...instrumentMeta,\n    };\n    spectra.push(new Spectrum(data.x, data.y, spectra.length + 1, { meta }));\n  }\n  return spectra;\n}\n\nfunction parseDate(line) {\n  let dateString = line\n    .replace('X d_t=', '')\n    .trim()\n    .replace(/\"/g, '');\n  let date = new Date(dateString);\n  return date;\n}\n\nfunction parseScale(line, nbValues) {\n  let result = {};\n  line = line.replace(/ ([xy]) /g, ',$1,');\n  let parts = line.split('; ');\n\n  for (let part of parts) {\n    let parsedPart = parseScalePart(part, nbValues);\n    result[parsedPart.axis] = parsedPart;\n  }\n  return result;\n}\n\nfunction parseS(lines) {\n  let result = {};\n  for (let line of lines) {\n    let key = line.replace(/X ._([^=]*)=(.*)/, '$1').trim();\n    key = getFieldName(key);\n    let value = line.replace(/X ._([^=]*)=(.*)/, '$2').trim();\n    value = value.replace(/^\"(.*)\"$/, '$1');\n    if (!isNaN(value)) value = Number(value);\n    result[key] = value;\n  }\n  return result;\n}\n\nfunction parseV(lines) {\n  let result = {};\n  for (let line of lines) {\n    let key = line.replace(/X ._([^=]*)=(.*)/, '$1').trim();\n    key = getFieldName(key);\n    let value = line.replace(/X ._([^=]*)=(.*)/, '$2').trim();\n    value = value.replace(/^\"(.*)\"$/, '$1');\n    if (!isNaN(value)) value = Number(value);\n    result[key] = value;\n  }\n  return result;\n}\n\nfunction parseScalePart(scale, nbValues) {\n  let parts = scale.split(',');\n  let result = {};\n  result.axis = parts[1];\n  result.kind = parts[0];\n  result.unit = parts[4].replace(/\"/g, '');\n  if (result.kind === 'SetScale/P') {\n    let from = Number(parts[2]);\n    let step = Number(parts[3]);\n    let values = [];\n    for (let i = 0; i < nbValues; i++) {\n      values.push(from);\n      from += step;\n      result.values = values;\n    }\n  }\n  return result;\n}\n\nfunction getFieldName(key) {\n  const mapping = {\n    CE: 'counterElectrodeType',\n    Calibrationfile: 'calibrationFile',\n    Username: 'username',\n    WE: 'workingElectrodeGlass',\n    cellname: 'cellname',\n    electrolyte: 'electrolyteZ960',\n    layer: 'semicondutorLayer',\n    specification: 'remarks',\n    temp: 'workingTemperature',\n    type: 'typeOfCell',\n    AR: 'cellActiveArea',\n    IT: 'powerIn',\n  };\n  return mapping[key] || key;\n}\n\n/**\n * Creates a new Chromatogram element based in a JCAMP string\n * @param {string} jcamp - String containing the JCAMP data\n * @return {Spectrum} - New class element with the given data\n */\nfunction fromJcamp(jcamp, id) {\n  const converted = jcampconverter.convert(jcamp, {\n    xy: true,\n    keepRecordsRegExp: /.*/,\n    canonicDataLabels: false,\n    dynamicTyping: true,\n  });\n\n  let data = converted.spectra[0].data[0];\n  let info = converted.info;\n  let meta = {};\n  for (let key of Object.keys(info).filter((key) => key.startsWith('$'))) {\n    meta[key.substr(1)] = info[key];\n  }\n  return new Spectrum(data.x, data.y, id, { meta });\n}\n\nexports.Spectrum = Spectrum;\nexports.fromJcamp = fromJcamp;\nexports.fromSIV = fromSIV;\n"],"names":["parseXY","text","options","rescale","uniqueX","xColumn","yColumn","keepInfo","maxNumberColumns","Number","MAX_SAFE_INTEGER","minNumberColumns","Math","max","lines","split","maxY","MIN_VALUE","result","x","y","info","l","length","line","trim","match","fields","parseFloat","replace","push","position","value","points","Error","current","counter","i","uniqueXFunction","data","fromXxyyArray","fromXyxyArray","point","fromXyxyObject","xyConvert","inputFormat","outputFormat","middleData","Array","isArray","TypeError","fromGeneral","toXxyyArray","ans","index","toXyxyArray","toXyxyObject","creator","meta","title","owner","origin","type","xUnit","yUnit","firstX","MAX_VALUE","lastX","firstY","lastY","header","key","Object","keys","join","parserOptions","parsed","map","getConverter","ntuplesSeparator","GC_MS_FIELDS","convertToFloatArray","stringArray","floatArray","Spectrum","defaultOptions","keepRecordsRegExp","canonicDataLabels","dynamicTyping","xy","withoutXY","chromatogram","keepSpectra","noContour","nbContourLevels","noiseMultiplier","profiling","convertMSFieldToLabel","toLowerCase","isMSField","canonicDataLabel","indexOf","prepareSpectrum","spectrum","xFactor","yFactor","observeFrequency","toUpperCase","deltaX","shiftOffsetVal","shift","getMedian","sort","compareNumbers","floor","a","b","fastParseXYData","isXYdata","currentData","ascii","currentX","currentY","endLine","charCodeAt","newLine","isDifference","isLastDifference","lastDifference","isDuplicate","inComment","currentValue","lastValue","isNegative","inValue","skipFirstValue","decimalPosition","pow","duplicate","j","ascii2","parseXYA","values","removeSymbolRegExp","isXYAdata","parsePeakTable","removeCommentRegExp","peakTableSplitRegExp","isPeaktable","logs","jcamp","ldr","dataValue","ldrs","infos","wantXY","assign","start","Date","now","ntuples","spectra","action","time","dataLabel","substring","xIndex","yIndex","firstVariable","secondVariable","symbol","first","last","vardim","nbPoints","factor","units","datatable","dataType","twoD","maxX","minX","minY","xType","indirectFrequency","shiftOffsetNum","varname","vartype","varform","min","yType","page","pageValue","pageSymbol","pageSymbolIndex","unit","sampleDescription","label","isNaN","newNtuples","zData","minZ","maxZ","ySize","xSize","z","xVector","reverse","noise","abs","convertTo3DZ","contourLines","povarHeight0","povarHeight1","povarHeight2","povarHeight3","isOver0","isOver1","isOver2","isOver3","pAx","pAy","pBx","pBy","lineZValue","nbSubSpectra","nbPovars","x0","dx","y0","dy","iter","contourLevels","level","contourLevel","side","exp","zValue","iSubSpectra","subSpectra","subSpectraAfter","povar","segments","generateContourLines","minMax","add2D","newData","k","times","series","ms","dimension","existingGCMSFields","complexChromatogram","slice","intensity","simpleChromatogram","convert","worker","stamps","input","useWorker","workerURL","URL","createObjectURL","Blob","toString","Worker","revokeObjectURL","addEventListener","event","JSON","parse","stamp","output","createWorker","Promise","resolve","random","postMessage","stringify","postToWorker","createTree","flatten","flat","stack","ntupleLevel","spaces","includes","labelLine","startsWith","children","finished","pop","undefined","forEach","entry","defineProperty","exports","constructor","id","getXLabel","getYLabel","parseScale","nbValues","parts","part","parsedPart","parseScalePart","axis","scale","kind","from","step","getFieldName","CE","Calibrationfile","Username","WE","cellname","electrolyte","layer","specification","temp","AR","IT","prototype","getData","this","toJcamp","convertToJcamp","fromJSON","converted","jcampconverter","filter","substr","content","allLines","sampleMeta","parseS","instrumentMeta","parseV","date","dateString","parseDate","ys","console","log","experiment"],"mappings":"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"}